diff options
189 files changed, 5535 insertions, 2451 deletions
diff --git a/Android.mk b/Android.mk index eb649c9152c3..634272b1d7ea 100644 --- a/Android.mk +++ b/Android.mk @@ -102,6 +102,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IUserSwitchObserver.aidl \ core/java/android/app/IWallpaperManager.aidl \ core/java/android/app/IWallpaperManagerCallback.aidl \ + core/java/android/app/admin/IDeviceAdminService.aidl \ core/java/android/app/admin/IDevicePolicyManager.aidl \ core/java/android/app/trust/IStrongAuthTracker.aidl \ core/java/android/app/trust/ITrustManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 392b60dc9c80..5926e9876364 100644 --- a/api/current.txt +++ b/api/current.txt @@ -104,7 +104,7 @@ package android { field public static final java.lang.String READ_FRAME_BUFFER = "android.permission.READ_FRAME_BUFFER"; field public static final deprecated java.lang.String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE"; field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS"; - field public static final java.lang.String READ_PHONE_NUMBER = "android.permission.READ_PHONE_NUMBER"; + field public static final java.lang.String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; field public static final java.lang.String READ_SMS = "android.permission.READ_SMS"; field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; @@ -3675,7 +3675,8 @@ package android.app { method public boolean onMenuItemSelected(int, android.view.MenuItem); method public boolean onMenuOpened(int, android.view.Menu); method public void onMovedToDisplay(int, android.content.res.Configuration); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onNavigateUp(); method public boolean onNavigateUpFromChild(android.app.Activity); method protected void onNewIntent(android.content.Intent); @@ -3683,7 +3684,8 @@ package android.app { method public void onOptionsMenuClosed(android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method protected void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method protected void onPostCreate(android.os.Bundle); method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle); method protected void onPostResume(); @@ -4180,7 +4182,7 @@ package android.app { field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts"; field public static final java.lang.String OPSTR_READ_CONTACTS = "android:read_contacts"; field public static final java.lang.String OPSTR_READ_EXTERNAL_STORAGE = "android:read_external_storage"; - field public static final java.lang.String OPSTR_READ_PHONE_NUMBER = "android:read_phone_number"; + field public static final java.lang.String OPSTR_READ_PHONE_NUMBERS = "android:read_phone_numbers"; field public static final java.lang.String OPSTR_READ_PHONE_STATE = "android:read_phone_state"; field public static final java.lang.String OPSTR_READ_SMS = "android:read_sms"; field public static final java.lang.String OPSTR_RECEIVE_MMS = "android:receive_mms"; @@ -4616,11 +4618,13 @@ package android.app { method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle); method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle); method public void onLowMemory(); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onOptionsItemSelected(android.view.MenuItem); method public void onOptionsMenuClosed(android.view.Menu); method public void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method public void onPrepareOptionsMenu(android.view.Menu); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method public void onResume(); @@ -4705,11 +4709,13 @@ package android.app { method public void dispatchDestroy(); method public void dispatchDestroyView(); method public void dispatchLowMemory(); - method public void dispatchMultiWindowModeChanged(boolean); + method public deprecated void dispatchMultiWindowModeChanged(boolean); + method public void dispatchMultiWindowModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchOptionsItemSelected(android.view.MenuItem); method public void dispatchOptionsMenuClosed(android.view.Menu); method public void dispatchPause(); - method public void dispatchPictureInPictureModeChanged(boolean); + method public deprecated void dispatchPictureInPictureModeChanged(boolean); + method public void dispatchPictureInPictureModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchPrepareOptionsMenu(android.view.Menu); method public void dispatchResume(); method public void dispatchStart(); @@ -6226,6 +6232,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6392,6 +6403,7 @@ package android.app.admin { method public void wipeData(int); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; @@ -9264,6 +9276,8 @@ package android.content { field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT"; field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"; field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW"; + field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED"; field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK"; @@ -9463,8 +9477,9 @@ package android.content { field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME"; field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; - field public static final deprecated java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; + field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT"; + field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.intent.extra.SUBSCRIPTION_INDEX"; field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -9823,6 +9838,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10327,12 +10343,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); - method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); + method public deprecated android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 - field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 + field public static final deprecated int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -10629,6 +10645,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -10882,9 +10899,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); - method public android.content.ComponentName[] getChooserComponentNames(); - method public android.os.PersistableBundle getChooserExtras(); - method public android.content.IntentFilter[] getChooserIntentFilters(); + method public deprecated android.content.ComponentName[] getChooserComponentNames(); + method public deprecated android.os.PersistableBundle getChooserExtras(); + method public deprecated android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -10897,7 +10914,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); - method public boolean isChooser(); + method public deprecated boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -10910,11 +10927,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); - method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); + method public deprecated android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); - method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); + method public deprecated android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -12687,7 +12704,7 @@ package android.graphics { enum_constant public static final android.graphics.Canvas.VertexMode TRIANGLE_STRIP; } - public final class Color { + public class Color { ctor public Color(); method public static int HSVToColor(float[]); method public static int HSVToColor(int, float[]); @@ -15966,11 +15983,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -22983,6 +23000,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -24525,19 +24543,19 @@ package android.media.tv { } public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 field public static final java.lang.String COLUMN_AUTHOR = "author"; field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; @@ -24555,28 +24573,28 @@ package android.media.tv { field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 } public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { @@ -24755,10 +24773,10 @@ package android.media.tv { field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public final class TvInputInfo implements android.os.Parcelable { @@ -39991,7 +40009,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); @@ -40004,7 +40022,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -40080,14 +40098,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -45992,7 +46013,6 @@ package android.view { method public static deprecated int getEdgeSlop(); method public static deprecated int getFadingEdgeLength(); method public static deprecated long getGlobalActionKeyTimeout(); - method public float getScaledHorizontalScrollFactor(); method public static int getJumpTapTimeout(); method public static int getKeyRepeatDelay(); method public static int getKeyRepeatTimeout(); @@ -46004,6 +46024,7 @@ package android.view { method public int getScaledDoubleTapSlop(); method public int getScaledEdgeSlop(); method public int getScaledFadingEdgeLength(); + method public float getScaledHorizontalScrollFactor(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); method public int getScaledMinimumFlingVelocity(); @@ -46012,6 +46033,7 @@ package android.view { method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); method public int getScaledTouchSlop(); + method public float getScaledVerticalScrollFactor(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); method public static deprecated int getScrollBarSize(); @@ -46019,7 +46041,6 @@ package android.view { method public static float getScrollFriction(); method public static int getTapTimeout(); method public static deprecated int getTouchSlop(); - method public float getScaledVerticalScrollFactor(); method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); method public boolean hasPermanentMenuKey(); diff --git a/api/system-current.txt b/api/system-current.txt index ac1597f0162a..a5f10cd42c72 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -182,7 +182,7 @@ package android { field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS"; field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY"; field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE"; - field public static final java.lang.String READ_PHONE_NUMBER = "android.permission.READ_PHONE_NUMBER"; + field public static final java.lang.String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES"; @@ -3801,7 +3801,8 @@ package android.app { method public boolean onMenuItemSelected(int, android.view.MenuItem); method public boolean onMenuOpened(int, android.view.Menu); method public void onMovedToDisplay(int, android.content.res.Configuration); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onNavigateUp(); method public boolean onNavigateUpFromChild(android.app.Activity); method protected void onNewIntent(android.content.Intent); @@ -3809,7 +3810,8 @@ package android.app { method public void onOptionsMenuClosed(android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method protected void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method protected void onPostCreate(android.os.Bundle); method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle); method protected void onPostResume(); @@ -4325,7 +4327,7 @@ package android.app { field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts"; field public static final java.lang.String OPSTR_READ_CONTACTS = "android:read_contacts"; field public static final java.lang.String OPSTR_READ_EXTERNAL_STORAGE = "android:read_external_storage"; - field public static final java.lang.String OPSTR_READ_PHONE_NUMBER = "android:read_phone_number"; + field public static final java.lang.String OPSTR_READ_PHONE_NUMBERS = "android:read_phone_numbers"; field public static final java.lang.String OPSTR_READ_PHONE_STATE = "android:read_phone_state"; field public static final java.lang.String OPSTR_READ_SMS = "android:read_sms"; field public static final java.lang.String OPSTR_RECEIVE_MMS = "android:receive_mms"; @@ -4775,11 +4777,13 @@ package android.app { method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle); method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle); method public void onLowMemory(); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onOptionsItemSelected(android.view.MenuItem); method public void onOptionsMenuClosed(android.view.Menu); method public void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method public void onPrepareOptionsMenu(android.view.Menu); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method public void onResume(); @@ -4864,11 +4868,13 @@ package android.app { method public void dispatchDestroy(); method public void dispatchDestroyView(); method public void dispatchLowMemory(); - method public void dispatchMultiWindowModeChanged(boolean); + method public deprecated void dispatchMultiWindowModeChanged(boolean); + method public void dispatchMultiWindowModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchOptionsItemSelected(android.view.MenuItem); method public void dispatchOptionsMenuClosed(android.view.Menu); method public void dispatchPause(); - method public void dispatchPictureInPictureModeChanged(boolean); + method public deprecated void dispatchPictureInPictureModeChanged(boolean); + method public void dispatchPictureInPictureModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchPrepareOptionsMenu(android.view.Menu); method public void dispatchResume(); method public void dispatchStart(); @@ -6429,6 +6435,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6616,6 +6627,7 @@ package android.app.admin { field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; @@ -9763,6 +9775,8 @@ package android.content { field public static final java.lang.String ACTION_BUG_REPORT = "android.intent.action.BUG_REPORT"; field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL"; field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON"; + field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; + field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON"; field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP"; field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER"; @@ -9773,6 +9787,8 @@ package android.content { field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT"; field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"; field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW"; + field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED"; field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK"; @@ -9992,8 +10008,9 @@ package android.content { field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME"; field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME"; - field public static final deprecated java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; + field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT"; + field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.intent.extra.SUBSCRIPTION_INDEX"; field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -10354,6 +10371,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10949,12 +10967,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); - method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); + method public deprecated android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 - field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 + field public static final deprecated int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -11280,6 +11298,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -11598,9 +11617,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); - method public android.content.ComponentName[] getChooserComponentNames(); - method public android.os.PersistableBundle getChooserExtras(); - method public android.content.IntentFilter[] getChooserIntentFilters(); + method public deprecated android.content.ComponentName[] getChooserComponentNames(); + method public deprecated android.os.PersistableBundle getChooserExtras(); + method public deprecated android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -11613,7 +11632,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); - method public boolean isChooser(); + method public deprecated boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -11626,11 +11645,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); - method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); + method public deprecated android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); - method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); + method public deprecated android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -13417,7 +13436,7 @@ package android.graphics { enum_constant public static final android.graphics.Canvas.VertexMode TRIANGLE_STRIP; } - public final class Color { + public class Color { ctor public Color(); method public static int HSVToColor(float[]); method public static int HSVToColor(int, float[]); @@ -17424,11 +17443,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -22823,6 +22842,7 @@ package android.media { method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); + method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy); method public void dispatchMediaKeyEvent(android.view.KeyEvent); method public int generateAudioSessionId(); method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations(); @@ -24774,6 +24794,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -25824,8 +25845,10 @@ package android.media.audiopolicy { public static abstract class AudioPolicy.AudioPolicyFocusListener { ctor public AudioPolicy.AudioPolicyFocusListener(); + method public void onAudioFocusAbandon(android.media.AudioFocusInfo); method public void onAudioFocusGrant(android.media.AudioFocusInfo, int); method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean); + method public void onAudioFocusRequest(android.media.AudioFocusInfo, int); } public static abstract class AudioPolicy.AudioPolicyStatusListener { @@ -25840,6 +25863,7 @@ package android.media.audiopolicy { method public android.media.audiopolicy.AudioPolicy build(); method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener); method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener); + method public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException; } @@ -26464,19 +26488,19 @@ package android.media.tv { } public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 field public static final java.lang.String COLUMN_AUTHOR = "author"; field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; @@ -26494,28 +26518,28 @@ package android.media.tv { field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 } public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { @@ -26695,10 +26719,10 @@ package android.media.tv { field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public static final class TvContract.WatchedPrograms implements android.media.tv.TvContract.BaseTvColumns { @@ -43391,7 +43415,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataConnectivityPossible(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); @@ -43412,7 +43436,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method public void setDataEnabled(boolean); method public void setDataEnabled(int, boolean); @@ -43504,14 +43528,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -49449,7 +49476,6 @@ package android.view { method public static deprecated int getEdgeSlop(); method public static deprecated int getFadingEdgeLength(); method public static deprecated long getGlobalActionKeyTimeout(); - method public float getScaledHorizontalScrollFactor(); method public static int getJumpTapTimeout(); method public static int getKeyRepeatDelay(); method public static int getKeyRepeatTimeout(); @@ -49461,6 +49487,7 @@ package android.view { method public int getScaledDoubleTapSlop(); method public int getScaledEdgeSlop(); method public int getScaledFadingEdgeLength(); + method public float getScaledHorizontalScrollFactor(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); method public int getScaledMinimumFlingVelocity(); @@ -49469,6 +49496,7 @@ package android.view { method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); method public int getScaledTouchSlop(); + method public float getScaledVerticalScrollFactor(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); method public static deprecated int getScrollBarSize(); @@ -49476,7 +49504,6 @@ package android.view { method public static float getScrollFriction(); method public static int getTapTimeout(); method public static deprecated int getTouchSlop(); - method public float getScaledVerticalScrollFactor(); method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); method public boolean hasPermanentMenuKey(); diff --git a/api/test-current.txt b/api/test-current.txt index fc3c9e74e440..1b5eeedfdf12 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -104,7 +104,7 @@ package android { field public static final java.lang.String READ_FRAME_BUFFER = "android.permission.READ_FRAME_BUFFER"; field public static final deprecated java.lang.String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE"; field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS"; - field public static final java.lang.String READ_PHONE_NUMBER = "android.permission.READ_PHONE_NUMBER"; + field public static final java.lang.String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; field public static final java.lang.String READ_SMS = "android.permission.READ_SMS"; field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; @@ -3677,7 +3677,8 @@ package android.app { method public boolean onMenuItemSelected(int, android.view.MenuItem); method public boolean onMenuOpened(int, android.view.Menu); method public void onMovedToDisplay(int, android.content.res.Configuration); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onNavigateUp(); method public boolean onNavigateUpFromChild(android.app.Activity); method protected void onNewIntent(android.content.Intent); @@ -3685,7 +3686,8 @@ package android.app { method public void onOptionsMenuClosed(android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method protected void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method protected void onPostCreate(android.os.Bundle); method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle); method protected void onPostResume(); @@ -4192,7 +4194,7 @@ package android.app { field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts"; field public static final java.lang.String OPSTR_READ_CONTACTS = "android:read_contacts"; field public static final java.lang.String OPSTR_READ_EXTERNAL_STORAGE = "android:read_external_storage"; - field public static final java.lang.String OPSTR_READ_PHONE_NUMBER = "android:read_phone_number"; + field public static final java.lang.String OPSTR_READ_PHONE_NUMBERS = "android:read_phone_numbers"; field public static final java.lang.String OPSTR_READ_PHONE_STATE = "android:read_phone_state"; field public static final java.lang.String OPSTR_READ_SMS = "android:read_sms"; field public static final java.lang.String OPSTR_RECEIVE_MMS = "android:receive_mms"; @@ -4628,11 +4630,13 @@ package android.app { method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle); method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle); method public void onLowMemory(); - method public void onMultiWindowModeChanged(boolean); + method public void onMultiWindowModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onMultiWindowModeChanged(boolean); method public boolean onOptionsItemSelected(android.view.MenuItem); method public void onOptionsMenuClosed(android.view.Menu); method public void onPause(); - method public void onPictureInPictureModeChanged(boolean); + method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); + method public deprecated void onPictureInPictureModeChanged(boolean); method public void onPrepareOptionsMenu(android.view.Menu); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method public void onResume(); @@ -4717,11 +4721,13 @@ package android.app { method public void dispatchDestroy(); method public void dispatchDestroyView(); method public void dispatchLowMemory(); - method public void dispatchMultiWindowModeChanged(boolean); + method public deprecated void dispatchMultiWindowModeChanged(boolean); + method public void dispatchMultiWindowModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchOptionsItemSelected(android.view.MenuItem); method public void dispatchOptionsMenuClosed(android.view.Menu); method public void dispatchPause(); - method public void dispatchPictureInPictureModeChanged(boolean); + method public deprecated void dispatchPictureInPictureModeChanged(boolean); + method public void dispatchPictureInPictureModeChanged(boolean, android.content.res.Configuration); method public boolean dispatchPrepareOptionsMenu(android.view.Menu); method public void dispatchResume(); method public void dispatchStart(); @@ -6245,6 +6251,11 @@ package android.app.admin { field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } + public class DeviceAdminService extends android.app.Service { + ctor public DeviceAdminService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6420,6 +6431,7 @@ package android.app.admin { field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; + field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; @@ -9297,6 +9309,8 @@ package android.content { field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT"; field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"; field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW"; + field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED"; field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW"; field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK"; @@ -9496,8 +9510,9 @@ package android.content { field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT"; field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME"; field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; - field public static final deprecated java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; + field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT"; + field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.intent.extra.SUBSCRIPTION_INDEX"; field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -9856,6 +9871,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10363,12 +10379,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); - method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); + method public deprecated android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 - field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 + field public static final deprecated int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -10668,6 +10684,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -10922,9 +10939,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); - method public android.content.ComponentName[] getChooserComponentNames(); - method public android.os.PersistableBundle getChooserExtras(); - method public android.content.IntentFilter[] getChooserIntentFilters(); + method public deprecated android.content.ComponentName[] getChooserComponentNames(); + method public deprecated android.os.PersistableBundle getChooserExtras(); + method public deprecated android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -10937,7 +10954,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); - method public boolean isChooser(); + method public deprecated boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -10950,11 +10967,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); - method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); + method public deprecated android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); - method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); + method public deprecated android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -12737,7 +12754,7 @@ package android.graphics { enum_constant public static final android.graphics.Canvas.VertexMode TRIANGLE_STRIP; } - public final class Color { + public class Color { ctor public Color(); method public static int HSVToColor(float[]); method public static int HSVToColor(int, float[]); @@ -16021,11 +16038,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -23096,6 +23113,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -24638,19 +24656,19 @@ package android.media.tv { } public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 field public static final java.lang.String COLUMN_AUTHOR = "author"; field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; @@ -24668,28 +24686,28 @@ package android.media.tv { field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 } public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { @@ -24868,10 +24886,10 @@ package android.media.tv { field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public final class TvInputInfo implements android.os.Parcelable { @@ -37522,6 +37540,7 @@ package android.service.quicksettings { ctor public TileService(); method public final android.service.quicksettings.Tile getQsTile(); method public final boolean isLocked(); + method public static boolean isQuickSettingsSupported(); method public final boolean isSecure(); method public android.os.IBinder onBind(android.content.Intent); method public void onClick(); @@ -40194,7 +40213,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); @@ -40207,7 +40226,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -40283,14 +40302,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -46369,7 +46391,6 @@ package android.view { method public static deprecated int getEdgeSlop(); method public static deprecated int getFadingEdgeLength(); method public static deprecated long getGlobalActionKeyTimeout(); - method public float getScaledHorizontalScrollFactor(); method public static int getHoverTooltipHideShortTimeout(); method public static int getHoverTooltipHideTimeout(); method public static int getHoverTooltipShowTimeout(); @@ -46385,6 +46406,7 @@ package android.view { method public int getScaledDoubleTapSlop(); method public int getScaledEdgeSlop(); method public int getScaledFadingEdgeLength(); + method public float getScaledHorizontalScrollFactor(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); method public int getScaledMinimumFlingVelocity(); @@ -46393,6 +46415,7 @@ package android.view { method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); method public int getScaledTouchSlop(); + method public float getScaledVerticalScrollFactor(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); method public static deprecated int getScrollBarSize(); @@ -46400,7 +46423,6 @@ package android.view { method public static float getScrollFriction(); method public static int getTapTimeout(); method public static deprecated int getTouchSlop(); - method public float getScaledVerticalScrollFactor(); method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); method public boolean hasPermanentMenuKey(); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 3a92b9e74144..0e2c13ee1719 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -26,7 +26,8 @@ LOCAL_SHARED_LIBRARIES := \ libGLESv1_CM \ libgui \ libOpenSLES \ - libtinyalsa + libtinyalsa \ + libbase LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index a6d2986e185a..2435ffadd879 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -38,6 +38,8 @@ #include <utils/Log.h> #include <utils/SystemClock.h> +#include <android-base/properties.h> + #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -67,6 +69,9 @@ namespace android { static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip"; static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip"; +static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip"; +static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip"; + static const char SYSTEM_DATA_DIR_PATH[] = "/data/system"; static const char SYSTEM_TIME_DIR_NAME[] = "time"; static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time"; @@ -106,7 +111,13 @@ BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccu mSession = new SurfaceComposerClient(); // If the system has already booted, the animation is not being used for a boot. - mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0); + mSystemBoot = !android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false); + std::string powerCtl = android::base::GetProperty("sys.powerctl", ""); + if (powerCtl.empty()) { + mShuttingDown = false; + } else { + mShuttingDown = true; + } } void BootAnimation::onFirstRef() { @@ -314,16 +325,23 @@ status_t BootAnimation::readyToRun() { char decrypt[PROPERTY_VALUE_MAX]; property_get("vold.decrypt", decrypt, ""); - bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); + bool encryptedAnimation = atoi(decrypt) != 0 || + !strcmp("trigger_restart_min_framework", decrypt); - if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) { + if (!mShuttingDown && encryptedAnimation && + (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) { mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE; + return NO_ERROR; } - else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) { - mZipFileName = OEM_BOOTANIMATION_FILE; - } - else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) { - mZipFileName = SYSTEM_BOOTANIMATION_FILE; + static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE}; + static const char* shutdownFiles[] = + {OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE}; + + for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) { + if (access(f, R_OK) == 0) { + mZipFileName = f; + return NO_ERROR; + } } return NO_ERROR; } @@ -1047,7 +1065,9 @@ bool BootAnimation::playSoundsAllowed() const { if (!mSystemBoot) { return false; } - + if (mShuttingDown) { // no audio while shutting down + return false; + } // Read the system property to see if we should play the sound. // If it's not present, default to allowed. if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) { @@ -1073,7 +1093,7 @@ bool BootAnimation::updateIsTimeAccurate() { if (mTimeIsAccurate) { return true; } - + if (mShuttingDown) return true; struct stat statResult; if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) { diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index f1fc98e10c1e..181ef1c596d1 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -163,6 +163,7 @@ private: bool mTimeIsAccurate; bool mTimeFormat12Hour; bool mSystemBoot; + bool mShuttingDown; String8 mZipFileName; SortedVector<String8> mLoadedFiles; sp<TimeCheckThread> mTimeCheckThread = nullptr; diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 7344ba74f70b..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -1,7 +1,7 @@ service bootanim /system/bin/bootanimation - class core + class core animation user graphics group graphics audio disabled oneshot - writepid /dev/stune/top-app/tasks
\ No newline at end of file + writepid /dev/stune/top-app/tasks diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 5c7a12cf5eb9..fe496e398988 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -431,12 +431,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim // Force all the animations to end when the duration scale is 0. private void forceToEnd() { - // TODO: Below is commented out to temp work around b/36241584, uncomment this when it's - // fixed. -// if (mEndCanBeCalled) { -// end(); -// return; -// } + if (mEndCanBeCalled) { + end(); + return; + } // Note: we don't want to combine this case with the end() method below because in // the case of developer calling end(), we still need to make sure end() is explicitly diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 147b5d034fd1..d432160ccfbf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1889,11 +1889,32 @@ public class Activity extends ContextThemeWrapper /** * Called by the system when the activity changes from fullscreen mode to multi-window mode and + * visa-versa. This method provides the same configuration that will be sent in the following + * {@link #onConfigurationChanged(Configuration)} call after the activity enters this mode. + * + * @see android.R.attr#resizeableActivity + * + * @param isInMultiWindowMode True if the activity is in multi-window mode. + * @param newConfig The new configuration of the activity with the state + * {@param isInMultiWindowMode}. + */ + public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { + // Left deliberately empty. There should be no side effects if a direct + // subclass of Activity does not call super. + onMultiWindowModeChanged(isInMultiWindowMode); + } + + /** + * Called by the system when the activity changes from fullscreen mode to multi-window mode and * visa-versa. + * * @see android.R.attr#resizeableActivity * * @param isInMultiWindowMode True if the activity is in multi-window mode. + * + * @deprecated Use {@link #onMultiWindowModeChanged(boolean, Configuration)} instead. */ + @Deprecated public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { // Left deliberately empty. There should be no side effects if a direct // subclass of Activity does not call super. @@ -1914,11 +1935,33 @@ public class Activity extends ContextThemeWrapper } /** + * Called by the system when the activity changes to and from picture-in-picture mode. This + * method provides the same configuration that will be sent in the following + * {@link #onConfigurationChanged(Configuration)} call after the activity enters this mode. + * + * @see android.R.attr#supportsPictureInPicture + * + * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode. + * @param newConfig The new configuration of the activity with the state + * {@param isInPictureInPictureMode}. + */ + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + // Left deliberately empty. There should be no side effects if a direct + // subclass of Activity does not call super. + onPictureInPictureModeChanged(isInPictureInPictureMode); + } + + /** * Called by the system when the activity changes to and from picture-in-picture mode. + * * @see android.R.attr#supportsPictureInPicture * * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode. + * + * @deprecated Use {@link #onPictureInPictureModeChanged(boolean, Configuration)} instead. */ + @Deprecated public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { // Left deliberately empty. There should be no side effects if a direct // subclass of Activity does not call super. @@ -4116,14 +4159,25 @@ public class Activity extends ContextThemeWrapper mTaskDescription.setPrimaryColor(colorPrimary); } } - // For dev-preview only. - if (mTaskDescription.getBackgroundColor() == 0) { - int colorBackground = a.getColor( - com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0); - if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) { - mTaskDescription.setBackgroundColor(colorBackground); - } + + int colorBackground = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0); + if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) { + mTaskDescription.setBackgroundColor(colorBackground); } + + final int statusBarColor = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0); + if (statusBarColor != 0) { + mTaskDescription.setStatusBarColor(statusBarColor); + } + + final int navigationBarColor = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_navigationBarColor, 0); + if (navigationBarColor != 0) { + mTaskDescription.setNavigationBarColor(navigationBarColor); + } + a.recycle(); setTaskDescription(mTaskDescription); } @@ -6993,21 +7047,25 @@ public class Activity extends ContextThemeWrapper } } - final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) { + final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, + Configuration newConfig) { if (DEBUG_LIFECYCLE) Slog.v(TAG, - "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode); - mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode); + "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode + + " " + newConfig); + mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); if (mWindow != null) { mWindow.onMultiWindowModeChanged(); } - onMultiWindowModeChanged(isInMultiWindowMode); + onMultiWindowModeChanged(isInMultiWindowMode, newConfig); } - final void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) { + final void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { if (DEBUG_LIFECYCLE) Slog.v(TAG, - "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode); - mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode); - onPictureInPictureModeChanged(isInPictureInPictureMode); + "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode + + " " + newConfig); + mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4004bd6686b1..aede1bb67f80 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1145,6 +1145,8 @@ public class ActivityManager { private String mIconFilename; private int mColorPrimary; private int mColorBackground; + private int mStatusBarColor; + private int mNavigationBarColor; /** * Creates the TaskDescription to the specified values. @@ -1155,7 +1157,7 @@ public class ActivityManager { * opaque. */ public TaskDescription(String label, Bitmap icon, int colorPrimary) { - this(label, icon, null, colorPrimary, 0); + this(label, icon, null, colorPrimary, 0, 0, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1168,7 +1170,7 @@ public class ActivityManager { * @param icon An icon that represents the current state of this activity. */ public TaskDescription(String label, Bitmap icon) { - this(label, icon, null, 0, 0); + this(label, icon, null, 0, 0, 0, 0); } /** @@ -1177,24 +1179,26 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. */ public TaskDescription(String label) { - this(label, null, null, 0, 0); + this(label, null, null, 0, 0, 0, 0); } /** * Creates an empty TaskDescription. */ public TaskDescription() { - this(null, null, null, 0, 0); + this(null, null, null, 0, 0, 0, 0); } /** @hide */ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary, - int colorBackground) { + int colorBackground, int statusBarColor, int navigationBarColor) { mLabel = label; mIcon = icon; mIconFilename = iconFilename; mColorPrimary = colorPrimary; mColorBackground = colorBackground; + mStatusBarColor = statusBarColor; + mNavigationBarColor = navigationBarColor; } /** @@ -1214,6 +1218,8 @@ public class ActivityManager { mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; mColorBackground = other.mColorBackground; + mStatusBarColor = other.mStatusBarColor; + mNavigationBarColor = other.mNavigationBarColor; } private TaskDescription(Parcel source) { @@ -1253,6 +1259,20 @@ public class ActivityManager { } /** + * @hide + */ + public void setStatusBarColor(int statusBarColor) { + mStatusBarColor = statusBarColor; + } + + /** + * @hide + */ + public void setNavigationBarColor(int navigationBarColor) { + mNavigationBarColor = navigationBarColor; + } + + /** * Sets the icon for this task description. * @hide */ @@ -1325,6 +1345,20 @@ public class ActivityManager { return mColorBackground; } + /** + * @hide + */ + public int getStatusBarColor() { + return mStatusBarColor; + } + + /** + * @hide + */ + public int getNavigationBarColor() { + return mNavigationBarColor; + } + /** @hide */ public void saveToXml(XmlSerializer out) throws IOException { if (mLabel != null) { @@ -1377,6 +1411,8 @@ public class ActivityManager { } dest.writeInt(mColorPrimary); dest.writeInt(mColorBackground); + dest.writeInt(mStatusBarColor); + dest.writeInt(mNavigationBarColor); if (mIconFilename == null) { dest.writeInt(0); } else { @@ -1390,6 +1426,8 @@ public class ActivityManager { mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null; mColorPrimary = source.readInt(); mColorBackground = source.readInt(); + mStatusBarColor = source.readInt(); + mNavigationBarColor = source.readInt(); mIconFilename = source.readInt() > 0 ? source.readString() : null; } @@ -1407,7 +1445,9 @@ public class ActivityManager { public String toString() { return "TaskDescription Label: " + mLabel + " Icon: " + mIcon + " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary + - " colorBackground: " + mColorBackground; + " colorBackground: " + mColorBackground + + " statusBarColor: " + mColorBackground + + " navigationBarColor: " + mNavigationBarColor; } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b5d1fa8550df..d1d462c2fbe8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -221,6 +221,7 @@ public final class ActivityThread { private long mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; private ContextImpl mSystemContext; + private ContextImpl mSystemUiContext; static volatile IPackageManager sPackageManager; @@ -1411,15 +1412,23 @@ public final class ActivityThread { } @Override - public void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) - throws RemoteException { - sendMessage(H.MULTI_WINDOW_MODE_CHANGED, token, isInMultiWindowMode ? 1 : 0); + public void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode, + Configuration overrideConfig) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = overrideConfig; + args.argi1 = isInMultiWindowMode ? 1 : 0; + sendMessage(H.MULTI_WINDOW_MODE_CHANGED, args); } @Override - public void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode) - throws RemoteException { - sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, token, isInPipMode ? 1 : 0); + public void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode, + Configuration overrideConfig) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = overrideConfig; + args.argi1 = isInPipMode ? 1 : 0; + sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, args); } @Override @@ -1816,10 +1825,14 @@ public final class ActivityThread { handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj); break; case MULTI_WINDOW_MODE_CHANGED: - handleMultiWindowModeChanged((IBinder) msg.obj, msg.arg1 == 1); + handleMultiWindowModeChanged((IBinder) ((SomeArgs) msg.obj).arg1, + ((SomeArgs) msg.obj).argi1 == 1, + (Configuration) ((SomeArgs) msg.obj).arg2); break; case PICTURE_IN_PICTURE_MODE_CHANGED: - handlePictureInPictureModeChanged((IBinder) msg.obj, msg.arg1 == 1); + handlePictureInPictureModeChanged((IBinder) ((SomeArgs) msg.obj).arg1, + ((SomeArgs) msg.obj).argi1 == 1, + (Configuration) ((SomeArgs) msg.obj).arg2); break; case LOCAL_VOICE_INTERACTION_STARTED: handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1, @@ -2178,9 +2191,19 @@ public final class ActivityThread { } } + public ContextImpl getSystemUiContext() { + synchronized (this) { + if (mSystemUiContext == null) { + mSystemUiContext = ContextImpl.createSystemUiContext(this); + } + return mSystemUiContext; + } + } + public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { synchronized (this) { getSystemContext().installSystemApplicationInfo(info, classLoader); + getSystemUiContext().installSystemApplicationInfo(info, classLoader); // give ourselves a default profiler mProfiler = new Profiler(); @@ -3119,17 +3142,27 @@ public final class ActivityThread { } } - private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) { + private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode, + Configuration overrideConfig) { final ActivityClientRecord r = mActivities.get(token); if (r != null) { - r.activity.dispatchMultiWindowModeChanged(isInMultiWindowMode); + final Configuration newConfig = new Configuration(mConfiguration); + if (overrideConfig != null) { + newConfig.updateFrom(overrideConfig); + } + r.activity.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); } } - private void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode) { + private void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode, + Configuration overrideConfig) { final ActivityClientRecord r = mActivities.get(token); if (r != null) { - r.activity.dispatchPictureInPictureModeChanged(isInPipMode); + final Configuration newConfig = new Configuration(mConfiguration); + if (overrideConfig != null) { + newConfig.updateFrom(overrideConfig); + } + r.activity.dispatchPictureInPictureModeChanged(isInPipMode, newConfig); } } @@ -5009,6 +5042,11 @@ public final class ActivityThread { if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { systemTheme.rebase(); } + + final Theme systemUiTheme = getSystemUiContext().getTheme(); + if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) { + systemUiTheme.rebase(); + } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); @@ -5064,9 +5102,10 @@ public final class ActivityThread { // Trigger a regular Configuration change event, only with a different assetsSeq number // so that we actually call through to all components. + // TODO(adamlesinski): Change this to make use of ActivityManager's upcoming ability to + // store configurations per-process. Configuration newConfig = new Configuration(); - newConfig.unset(); - newConfig.assetsSeq = mConfiguration.assetsSeq + 1; + newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1; handleConfigurationChanged(newConfig, null); // Schedule all activities to reload diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index cbd7b9d4aa9c..82921524457f 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -242,7 +242,7 @@ public class AppOpsManager { /** @hide */ public static final int OP_AUDIO_ACCESSIBILITY_VOLUME = 64; /** @hide Read the phone number. */ - public static final int OP_READ_PHONE_NUMBER = 65; + public static final int OP_READ_PHONE_NUMBERS = 65; /** @hide Request package installs through package installer */ public static final int OP_REQUEST_INSTALL_PACKAGES = 66; /** @hide Enter picture-in-picture. */ @@ -353,8 +353,8 @@ public class AppOpsManager { /** @hide Get device accounts. */ public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; - public static final String OPSTR_READ_PHONE_NUMBER - = "android:read_phone_number"; + public static final String OPSTR_READ_PHONE_NUMBERS + = "android:read_phone_numbers"; /** Access to picture-in-picture. */ public static final String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; @@ -391,7 +391,7 @@ public class AppOpsManager { OP_FINE_LOCATION, // Phone OP_READ_PHONE_STATE, - OP_READ_PHONE_NUMBER, + OP_READ_PHONE_NUMBERS, OP_CALL_PHONE, OP_READ_CALL_LOG, OP_WRITE_CALL_LOG, @@ -487,7 +487,7 @@ public class AppOpsManager { OP_GET_ACCOUNTS, OP_RUN_IN_BACKGROUND, OP_AUDIO_ACCESSIBILITY_VOLUME, - OP_READ_PHONE_NUMBER, + OP_READ_PHONE_NUMBERS, OP_REQUEST_INSTALL_PACKAGES, OP_PICTURE_IN_PICTURE, OP_INSTANT_APP_START_FOREGROUND, @@ -564,7 +564,7 @@ public class AppOpsManager { OPSTR_GET_ACCOUNTS, null, null, // OP_AUDIO_ACCESSIBILITY_VOLUME - OPSTR_READ_PHONE_NUMBER, + OPSTR_READ_PHONE_NUMBERS, null, // OP_REQUEST_INSTALL_PACKAGES OPSTR_PICTURE_IN_PICTURE, OPSTR_INSTANT_APP_START_FOREGROUND, @@ -641,7 +641,7 @@ public class AppOpsManager { "GET_ACCOUNTS", "RUN_IN_BACKGROUND", "AUDIO_ACCESSIBILITY_VOLUME", - "READ_PHONE_NUMBER", + "READ_PHONE_NUMBERS", "REQUEST_INSTALL_PACKAGES", "PICTURE_IN_PICTURE", "INSTANT_APP_START_FOREGROUND", @@ -718,7 +718,7 @@ public class AppOpsManager { Manifest.permission.GET_ACCOUNTS, null, // no permission for running in background null, // no permission for changing accessibility volume - Manifest.permission.READ_PHONE_NUMBER, + Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.REQUEST_INSTALL_PACKAGES, null, // no permission for entering picture-in-picture on hide Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, @@ -796,7 +796,7 @@ public class AppOpsManager { null, // GET_ACCOUNTS null, // RUN_IN_BACKGROUND UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ACCESSIBILITY_VOLUME - null, // READ_PHONE_NUMBER + null, // READ_PHONE_NUMBERS null, // REQUEST_INSTALL_PACKAGES null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE null, // INSTANT_APP_START_FOREGROUND @@ -873,7 +873,7 @@ public class AppOpsManager { false, // GET_ACCOUNTS false, // RUN_IN_BACKGROUND false, // AUDIO_ACCESSIBILITY_VOLUME - false, // READ_PHONE_NUMBER + false, // READ_PHONE_NUMBERS false, // REQUEST_INSTALL_PACKAGES false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE false, // INSTANT_APP_START_FOREGROUND diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 461f9cc35125..a6838f8bbf0a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1352,7 +1352,7 @@ public class ApplicationPackageManager extends PackageManager { public Resources getResourcesForApplication(@NonNull ApplicationInfo app) throws NameNotFoundException { if (app.packageName.equals("system")) { - return mContext.mMainThread.getSystemContext().getResources(); + return mContext.mMainThread.getSystemUiContext().getResources(); } final boolean sameUid = (app.uid == Process.myUid()); final Resources r = mContext.mMainThread.getTopLevelResources( @@ -1383,7 +1383,7 @@ public class ApplicationPackageManager extends PackageManager { "Call does not support special user #" + userId); } if ("system".equals(appPackageName)) { - return mContext.mMainThread.getSystemContext().getResources(); + return mContext.mMainThread.getSystemUiContext().getResources(); } try { ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, sDefaultFlags, userId); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5a7246a4ed38..75f9d671fbad 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2218,6 +2218,18 @@ class ContextImpl extends Context { return context; } + /** + * System Context to be used for UI. This Context has resources that can be themed. + */ + static ContextImpl createSystemUiContext(ActivityThread mainThread) { + LoadedApk packageInfo = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + null); + context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null, + packageInfo.getCompatibilityInfo())); + return context; + } + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 02fe101c9152..3102a93790b2 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1716,19 +1716,55 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene /** * Called when the Fragment's activity changes from fullscreen mode to multi-window mode and * visa-versa. This is generally tied to {@link Activity#onMultiWindowModeChanged} of the + * containing Activity. This method provides the same configuration that will be sent in the + * following {@link #onConfigurationChanged(Configuration)} call after the activity enters this + * mode. + * + * @param isInMultiWindowMode True if the activity is in multi-window mode. + * @param newConfig The new configuration of the activity with the state + * {@param isInMultiWindowMode}. + */ + public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { + onMultiWindowModeChanged(isInMultiWindowMode); + } + + /** + * Called when the Fragment's activity changes from fullscreen mode to multi-window mode and + * visa-versa. This is generally tied to {@link Activity#onMultiWindowModeChanged} of the * containing Activity. * * @param isInMultiWindowMode True if the activity is in multi-window mode. + * + * @deprecated Use {@link #onMultiWindowModeChanged(boolean, Configuration)} instead. */ + @Deprecated public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { } /** * Called by the system when the activity changes to and from picture-in-picture mode. This is * generally tied to {@link Activity#onPictureInPictureModeChanged} of the containing Activity. + * This method provides the same configuration that will be sent in the following + * {@link #onConfigurationChanged(Configuration)} call after the activity enters this mode. + * + * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode. + * @param newConfig The new configuration of the activity with the state + * {@param isInPictureInPictureMode}. + */ + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + onPictureInPictureModeChanged(isInPictureInPictureMode); + } + + /** + * Called by the system when the activity changes to and from picture-in-picture mode. This is + * generally tied to {@link Activity#onPictureInPictureModeChanged} of the containing Activity. * * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode. + * + * @deprecated Use {@link #onPictureInPictureModeChanged(boolean, Configuration)} instead. */ + @Deprecated public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { } @@ -2572,6 +2608,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } + @Deprecated void performMultiWindowModeChanged(boolean isInMultiWindowMode) { onMultiWindowModeChanged(isInMultiWindowMode); if (mChildFragmentManager != null) { @@ -2579,6 +2616,14 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } + void performMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { + onMultiWindowModeChanged(isInMultiWindowMode, newConfig); + if (mChildFragmentManager != null) { + mChildFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); + } + } + + @Deprecated void performPictureInPictureModeChanged(boolean isInPictureInPictureMode) { onPictureInPictureModeChanged(isInPictureInPictureMode); if (mChildFragmentManager != null) { @@ -2586,6 +2631,15 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } + void performPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + if (mChildFragmentManager != null) { + mChildFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, + newConfig); + } + } + void performConfigurationChanged(Configuration newConfig) { onConfigurationChanged(newConfig); if (mChildFragmentManager != null) { diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java index 9ea15a04e150..cff94d8cbf2d 100644 --- a/core/java/android/app/FragmentController.java +++ b/core/java/android/app/FragmentController.java @@ -250,23 +250,52 @@ public class FragmentController { * <p>Call when the multi-window mode of the activity changed. * * @see Fragment#onMultiWindowModeChanged + * @deprecated use {@link #dispatchMultiWindowModeChanged(boolean, Configuration)} */ + @Deprecated public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) { mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode); } /** + * Lets all Fragments managed by the controller's FragmentManager know the multi-window mode of + * the activity changed. + * <p>Call when the multi-window mode of the activity changed. + * + * @see Fragment#onMultiWindowModeChanged + */ + public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, + Configuration newConfig) { + mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); + } + + /** * Lets all Fragments managed by the controller's FragmentManager know the picture-in-picture * mode of the activity changed. * <p>Call when the picture-in-picture mode of the activity changed. * * @see Fragment#onPictureInPictureModeChanged + * @deprecated use {@link #dispatchPictureInPictureModeChanged(boolean, Configuration)} */ + @Deprecated public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) { mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode); } /** + * Lets all Fragments managed by the controller's FragmentManager know the picture-in-picture + * mode of the activity changed. + * <p>Call when the picture-in-picture mode of the activity changed. + * + * @see Fragment#onPictureInPictureModeChanged + */ + public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, + newConfig); + } + + /** * Lets all Fragments managed by the controller's FragmentManager * know a configuration change occurred. * <p>Call when there is a configuration change. diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 0c1be07ab231..279b9003f8c0 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1844,6 +1844,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } synchronized (this) { if (mDestroyed || mHost == null) { + if (allowStateLoss) { + // This FragmentManager isn't attached, so drop the entire transaction. + return; + } throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { @@ -1960,6 +1964,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } public void execSingleAction(OpGenerator action, boolean allowStateLoss) { + if (allowStateLoss && (mHost == null || mDestroyed)) { + // This FragmentManager isn't attached, so drop the entire transaction. + return; + } ensureExecReady(allowStateLoss); if (action.generateOps(mTmpRecords, mTmpIsPop)) { mExecutingActions = true; @@ -2944,6 +2952,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } + /** + * @deprecated use {@link #dispatchMultiWindowModeChanged(boolean, Configuration)} + */ + @Deprecated public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) { if (mAdded == null) { return; @@ -2956,6 +2968,23 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } + public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, + Configuration newConfig) { + if (mAdded == null) { + return; + } + for (int i = mAdded.size() - 1; i >= 0; --i) { + final Fragment f = mAdded.get(i); + if (f != null) { + f.performMultiWindowModeChanged(isInMultiWindowMode, newConfig); + } + } + } + + /** + * @deprecated use {@link #dispatchPictureInPictureModeChanged(boolean, Configuration)} + */ + @Deprecated public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (mAdded == null) { return; @@ -2968,6 +2997,19 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } + public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + if (mAdded == null) { + return; + } + for (int i = mAdded.size() - 1; i >= 0; --i) { + final Fragment f = mAdded.get(i); + if (f != null) { + f.performPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + } + } + } + public void dispatchConfigurationChanged(Configuration newConfig) { if (mAdded != null) { for (int i=0; i<mAdded.size(); i++) { diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index e99691d5a8de..6c43fe3beca1 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -146,9 +146,10 @@ oneway interface IApplicationThread { void notifyCleartextNetwork(in byte[] firstPacket); void startBinderTracking(); void stopBinderTrackingAndDump(in ParcelFileDescriptor fd); - void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode); - void schedulePictureInPictureModeChanged(IBinder token, - boolean isInPictureInPictureMode); + void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode, + in Configuration newConfig); + void schedulePictureInPictureModeChanged(IBinder token, boolean isInPictureInPictureMode, + in Configuration newConfig); void scheduleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor voiceInteractor); void handleTrustStorageUpdate(); diff --git a/core/java/android/app/IServiceConnection.aidl b/core/java/android/app/IServiceConnection.aidl index 6804071f26eb..97042aa2919f 100644 --- a/core/java/android/app/IServiceConnection.aidl +++ b/core/java/android/app/IServiceConnection.aidl @@ -21,6 +21,6 @@ import android.content.ComponentName; /** @hide */ oneway interface IServiceConnection { - void connected(in ComponentName name, IBinder service); + void connected(in ComponentName name, IBinder service, boolean dead); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index dbed1beb4cf7..4205db098ef6 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -88,8 +88,8 @@ final class ServiceConnectionLeaked extends AndroidRuntimeException { * @hide */ public final class LoadedApk { - - private static final String TAG = "LoadedApk"; + static final String TAG = "LoadedApk"; + static final boolean DEBUG = false; private final ActivityThread mActivityThread; final String mPackageName; @@ -641,8 +641,7 @@ public final class LoadedApk { final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : TextUtils.join(File.pathSeparator, zipPaths); - if (ActivityThread.localLOGV) - Slog.v(ActivityThread.TAG, "Class path: " + zip + + if (DEBUG) Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + librarySearchPath); boolean needToSetupJitProfiles = false; @@ -1371,12 +1370,14 @@ public final class LoadedApk { LoadedApk.ServiceDispatcher sd = null; ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context); if (map != null) { + if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c); sd = map.get(c); } if (sd == null) { sd = new ServiceDispatcher(c, context, handler, flags); + if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c); if (map == null) { - map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>(); + map = new ArrayMap<>(); mServices.put(context, map); } map.put(c, sd); @@ -1396,6 +1397,7 @@ public final class LoadedApk { if (map != null) { sd = map.get(c); if (sd != null) { + if (DEBUG) Slog.d(TAG, "Removing dispatcher " + sd + " for conn " + c); map.remove(c); sd.doForget(); if (map.size() == 0) { @@ -1461,10 +1463,11 @@ public final class LoadedApk { mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd); } - public void connected(ComponentName name, IBinder service) throws RemoteException { + public void connected(ComponentName name, IBinder service, boolean dead) + throws RemoteException { LoadedApk.ServiceDispatcher sd = mDispatcher.get(); if (sd != null) { - sd.connected(name, service); + sd.connected(name, service, dead); } } } @@ -1533,23 +1536,23 @@ public final class LoadedApk { return mUnbindLocation; } - public void connected(ComponentName name, IBinder service) { + public void connected(ComponentName name, IBinder service, boolean dead) { if (mActivityThread != null) { - mActivityThread.post(new RunConnection(name, service, 0)); + mActivityThread.post(new RunConnection(name, service, 0, dead)); } else { - doConnected(name, service); + doConnected(name, service, dead); } } public void death(ComponentName name, IBinder service) { if (mActivityThread != null) { - mActivityThread.post(new RunConnection(name, service, 1)); + mActivityThread.post(new RunConnection(name, service, 1, false)); } else { doDeath(name, service); } } - public void doConnected(ComponentName name, IBinder service) { + public void doConnected(ComponentName name, IBinder service, boolean dead) { ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; @@ -1594,6 +1597,9 @@ public final class LoadedApk { if (old != null) { mConnection.onServiceDisconnected(name); } + if (dead) { + mConnection.onBindingDead(name); + } // If there is a new service, it is now connected. if (service != null) { mConnection.onServiceConnected(name, service); @@ -1616,15 +1622,16 @@ public final class LoadedApk { } private final class RunConnection implements Runnable { - RunConnection(ComponentName name, IBinder service, int command) { + RunConnection(ComponentName name, IBinder service, int command, boolean dead) { mName = name; mService = service; mCommand = command; + mDead = dead; } public void run() { if (mCommand == 0) { - doConnected(mName, mService); + doConnected(mName, mService, mDead); } else if (mCommand == 1) { doDeath(mName, mService); } @@ -1633,6 +1640,7 @@ public final class LoadedApk { final ComponentName mName; final IBinder mService; final int mCommand; + final boolean mDead; } private final class DeathMonitor implements IBinder.DeathRecipient diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index b42df5e2e0fb..489a0f0975ae 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -984,7 +984,7 @@ public class ResourcesManager { final ResourcesKey key = mResourceImpls.keyAt(i); final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; - if (impl != null && key.mResDir != null && key.mResDir.equals(baseCodePath)) { + if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, diff --git a/core/java/android/app/admin/DeviceAdminService.java b/core/java/android/app/admin/DeviceAdminService.java new file mode 100644 index 000000000000..cd0b1bf3d0a8 --- /dev/null +++ b/core/java/android/app/admin/DeviceAdminService.java @@ -0,0 +1,56 @@ +/* + * 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.app.admin; + +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.os.IBinder; + +/** + * Base class for a service that device owner/profile owners can optionally have. + * + * <p>The system searches for it with an intent filter with the + * {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} action, and tries to keep a bound + * connection as long as the hosting user is running, so that the device/profile owner is always + * considered to be in the foreground. + * + * <p>Device/profile owners can use + * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)} + * to disable/enable its own service. For example, when a device/profile owner no longer needs + * to be in the foreground, it can (and should) disable its service. + * + * <p>The service must not be exported. + * + * <p>TODO: Describe how the system handles crashes in DO/PO. + */ +public class DeviceAdminService extends Service { + private final IDeviceAdminServiceImpl mImpl; + + public DeviceAdminService() { + mImpl = new IDeviceAdminServiceImpl(); + } + + @Override + public final IBinder onBind(Intent intent) { + return mImpl.asBinder(); + } + + private class IDeviceAdminServiceImpl extends IDeviceAdminService.Stub { + } + + // So far, we have no methods in this class. +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 6d8d5e93c1ee..2f0a630bcd04 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1515,6 +1515,16 @@ public class DevicePolicyManager { public @interface ProvisioningPreCondition {} /** + * Service action: Action for a service that device owner and profile owner can optionally + * own. If a device owner or a profile owner has such a service, the system tries to keep + * a bound connection to it, in order to keep their process always running. + * The service must not be exported. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_DEVICE_ADMIN_SERVICE + = "android.app.action.DEVICE_ADMIN_SERVICE"; + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. diff --git a/core/java/android/app/admin/IDeviceAdminService.aidl b/core/java/android/app/admin/IDeviceAdminService.aidl new file mode 100644 index 000000000000..5276ed599226 --- /dev/null +++ b/core/java/android/app/admin/IDeviceAdminService.aidl @@ -0,0 +1,23 @@ +/* + * 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.app.admin; + +/** + * @hide + */ +interface IDeviceAdminService { +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index dbbfe308868c..be267b308bf3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4631,4 +4631,18 @@ public abstract class Context { public Handler getMainThreadHandler() { throw new RuntimeException("Not implemented. Must override in a subclass."); } + + /** + * Throws an exception if the Context is using system resources, + * which are non-runtime-overlay-themable and may show inconsistent UI. + * @hide + */ + public void assertRuntimeOverlayThemable() { + // Resources.getSystem() is a singleton and the only Resources not managed by + // ResourcesManager; therefore Resources.getSystem() is not themable. + if (getResources() == Resources.getSystem()) { + throw new IllegalArgumentException("Non-UI context used to display UI; " + + "get a UI context from ActivityThread#getSystemUiContext()"); + } + } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 116224b35d06..da887af52b2a 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -16,8 +16,6 @@ package android.content; -import static android.content.ContentProvider.maybeAddUserId; - import android.annotation.AnyRes; import android.annotation.BroadcastBehavior; import android.annotation.IntDef; @@ -43,6 +41,7 @@ import android.os.ResultReceiver; import android.os.ShellCommand; import android.os.StrictMode; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.provider.DocumentsContract; import android.provider.DocumentsProvider; import android.provider.MediaStore; @@ -50,9 +49,7 @@ import android.provider.OpenableColumns; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; - import com.android.internal.util.XmlUtils; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -70,6 +67,8 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; +import static android.content.ContentProvider.maybeAddUserId; + /** * An intent is an abstract description of an operation to be performed. It * can be used with {@link Context#startActivity(Intent) startActivity} to @@ -1093,6 +1092,8 @@ public class Intent implements Parcelable, Cloneable { * <p>Output: nothing. * @hide */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; /** * Activity action: Perform a call to any number (emergency or not) @@ -1102,6 +1103,8 @@ public class Intent implements Parcelable, Cloneable { * <p>Output: nothing. * @hide */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; /** @@ -3349,6 +3352,32 @@ public class Intent implements Parcelable, Cloneable { ACTION_DYNAMIC_SENSOR_CHANGED = "android.intent.action.DYNAMIC_SENSOR_CHANGED"; /** + * Broadcast Action: The default subscription has changed. This has the following + * extra values:</p> + * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED + = "android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED"; + + /** + * Broadcast Action: The default sms subscription has changed. This has the following + * extra values:</p> + * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms + * subscription index + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED + = "android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + + /** + * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and + * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription + * which has changed. + */ + public static final String EXTRA_SUBSCRIPTION_INDEX = "android.intent.extra.SUBSCRIPTION_INDEX"; + + /** * Deprecated - use {@link #ACTION_FACTORY_RESET} instead. * * {@hide} @@ -3868,23 +3897,9 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT"; /** - * A content: URI holding a stream of data associated with the Intent, used - * with {@link #ACTION_SEND} to supply the data being sent. - * <p> - * Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN} this value - * will be automatically promoted to {@link Intent#setClipData(ClipData)} - * when that value is not already defined. - * <p> - * Starting in {@link android.os.Build.VERSION_CODES#O} this value will be - * automatically demoted from {@link Intent#getClipData()} when this value - * is not already defined. - * - * @deprecated apps should use {@link Intent#setClipData(ClipData)} and - * {@link Intent#getClipData()} instead of this extra, since - * only those APIs can extend temporary permission grants to the - * underlying resource. + * A content: URI holding a stream of data associated with the Intent, + * used with {@link #ACTION_SEND} to supply the data being sent. */ - @Deprecated public static final String EXTRA_STREAM = "android.intent.extra.STREAM"; /** @@ -9429,21 +9444,6 @@ public class Intent implements Parcelable, Cloneable { mContentUserHint = UserHandle.USER_CURRENT; } } - - // If someone is sending us ClipData, but not EXTRA_STREAM, offer to - // downgrade that content for older apps to find - if (mClipData != null && mClipData.getItemCount() > 0 && !hasExtra(EXTRA_STREAM)) { - final String action = getAction(); - if (ACTION_SEND.equals(action)) { - putExtra(EXTRA_STREAM, mClipData.getItemAt(0).getUri()); - } else if (ACTION_SEND_MULTIPLE.equals(action)) { - final ArrayList<Uri> list = new ArrayList<>(); - for (int i = 0; i < mClipData.getItemCount(); i++) { - list.add(mClipData.getItemAt(i).getUri()); - } - putExtra(EXTRA_STREAM, list); - } - } } /** diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java index d115ce43e93d..8e428f9e9b3d 100644 --- a/core/java/android/content/ServiceConnection.java +++ b/core/java/android/content/ServiceConnection.java @@ -37,7 +37,7 @@ public interface ServiceConnection { * @param service The IBinder of the Service's communication channel, * which you can now make calls on. */ - public void onServiceConnected(ComponentName name, IBinder service); + void onServiceConnected(ComponentName name, IBinder service); /** * Called when a connection to the Service has been lost. This typically @@ -49,5 +49,18 @@ public interface ServiceConnection { * @param name The concrete component name of the service whose * connection has been lost. */ - public void onServiceDisconnected(ComponentName name); + void onServiceDisconnected(ComponentName name); + + /** + * Called when the binding to this connection is dead. This means the + * interface will never receive another connection. The application will + * need to unbind and rebind the connection to activate it again. This may + * happen, for example, if the application hosting the service it is bound to + * has been updated. + * + * @param name The concrete component name of the service whose + * connection is dead. + */ + default void onBindingDead(ComponentName name) { + } } diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 41311eb04837..c08bd1db8302 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -55,8 +55,7 @@ interface ILauncherApps { String callingPackage, String packageName, int flags, in UserHandle user); ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, - in List shortcutIds, in ComponentName componentName, in Intent intent, int flags, - in UserHandle user); + in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user); void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); boolean startShortcut(String callingPackage, String packageName, String id, diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index abdef084aacc..4d767555e3b3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -275,11 +275,8 @@ public class LauncherApps { @Deprecated public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; - /** - * Include chooser shortcuts in the result. - * STOPSHIP TODO: Unless explicitly requesting chooser fields, we should strip out chooser - * relevant fields from the Shortcut. This should also be adequately documented. - */ + /** @deprecated punted, don't use. */ + @Deprecated public static final int FLAG_MATCH_CHOOSER = 1 << 4; /** @@ -319,7 +316,6 @@ public class LauncherApps { FLAG_MATCH_DYNAMIC, FLAG_MATCH_PINNED, FLAG_MATCH_MANIFEST, - FLAG_MATCH_CHOOSER, FLAG_GET_KEY_FIELDS_ONLY, }) @Retention(RetentionPolicy.SOURCE) @@ -336,9 +332,6 @@ public class LauncherApps { @Nullable ComponentName mActivity; - @Nullable - Intent mIntent; - @QueryFlags int mQueryFlags; @@ -382,11 +375,9 @@ public class LauncherApps { return this; } - /** - * If non-null, returns only shortcuts with intent filters that match this intent. - */ + /** @deprecated punted, don't use. */ + @Deprecated public ShortcutQuery setIntent(@Nullable Intent intent) { - mIntent = intent; return this; } @@ -428,7 +419,7 @@ public class LauncherApps { */ private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) { if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) { - Log.e(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); + Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); } } @@ -704,7 +695,7 @@ public class LauncherApps { try { return mService.getShortcuts(mContext.getPackageName(), query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, - query.mIntent, query.mQueryFlags, user) + query.mQueryFlags, user) .getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 136c13b6f37d..a493f33cfcb1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2158,7 +2158,6 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device supports only leanback UI. Only * applications designed for this experience should be run, though this is * not enforced by the system. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 16d582efb730..7bfde751e155 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -310,12 +310,18 @@ public abstract class PackageManagerInternal { List<String> overlayPackageNames); /** - * Resolves an intent, allowing instant apps to be resolved. + * Resolves an activity intent, allowing instant apps to be resolved. */ public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId); /** + * Resolves a service intent, allowing instant apps to be resolved. + */ + public abstract ResolveInfo resolveService(Intent intent, String resolvedType, + int flags, int userId, int callingUid); + + /** * Track the creator of a new isolated uid. * @param isolatedUid The newly created isolated uid. * @param ownerUid The uid of the app that created the isolated process. diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 520169499718..3f4a09072540 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.TaskStackBuilder; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -40,12 +39,10 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -99,14 +96,6 @@ public final class ShortcutInfo implements Parcelable { public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9; /** @hide */ - public static final int FLAG_CHOOSER = 1 << 10; - - /** - * TODO: Add FLAG_CHOOSER_INFO_OMITTED to reflect that chooser info was omitted in the Shortcut - * due to the context in which it was retrieved. - * TODO: Add a FLAG_LAUNCHABLE to reflect whether or not the Shortcut has a launchable intent - * @hide - */ @IntDef(flag = true, value = { FLAG_DYNAMIC, @@ -119,7 +108,6 @@ public final class ShortcutInfo implements Parcelable { FLAG_STRINGS_RESOLVED, FLAG_IMMUTABLE, FLAG_ADAPTIVE_BITMAP, - FLAG_CHOOSER, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} @@ -214,24 +202,6 @@ public final class ShortcutInfo implements Parcelable { @Nullable private PersistableBundle[] mIntentPersistableExtrases; - /** - * If used in a chooser, extras that should be added into the intent passed through. - */ - @Nullable - private PersistableBundle mChooserExtras; - - /** - * Intent filters to be used if the shortcut is to be used in a chooser context. - */ - @Nullable - private IntentFilter[] mChooserIntentFilters; - - /** - * Component names corresponding to the above intent filters. - */ - @Nullable - private ComponentName[] mChooserComponentNames; - private int mRank; /** @@ -281,13 +251,6 @@ public final class ShortcutInfo implements Parcelable { mDisabledMessageResId = b.mDisabledMessageResId; mCategories = cloneCategories(b.mCategories); mIntents = cloneIntents(b.mIntents); - if (b.mChooserIntentFilters != null) { - mChooserIntentFilters = b.mChooserIntentFilters.toArray(new IntentFilter[0]); - } - if (b.mChooserComponentNames != null) { - mChooserComponentNames = b.mChooserComponentNames.toArray(new ComponentName[0]); - } - mChooserExtras = b.mChooserExtras; fixUpIntentExtras(); mRank = b.mRank; mExtras = b.mExtras; @@ -368,28 +331,8 @@ public final class ShortcutInfo implements Parcelable { if (mTitle == null && mTitleResId == 0) { throw new IllegalArgumentException("Short label must be provided"); } - - // For a shortcut to be valid, there should either be an Intent, or a non-empty set of - // intent filters. - if (mIntents == null || mIntents.length == 0) { - Preconditions.checkNotNull(mChooserIntentFilters, - "Intent must be provided if not a chooser target"); - Preconditions.checkNotNull(mChooserComponentNames, - "Intent must be provided if not a chooser target"); - } - - // If ChooserIntentFilter are provided, they should match the length of the provided - // component names. - if (mChooserIntentFilters != null) { - if (mChooserComponentNames == null - || mChooserIntentFilters.length != mChooserComponentNames.length) { - throw new IllegalArgumentException("Inconsistent intent filters and " - + "component names given"); - } - if (mChooserIntentFilters.length == 0 || mChooserComponentNames.length == 0) { - throw new IllegalArgumentException("Empty intent filter and component names given"); - } - } + Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided"); + Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided"); } /** @@ -434,10 +377,6 @@ public final class ShortcutInfo implements Parcelable { mDisabledMessageResName = source.mDisabledMessageResName; mIconResName = source.mIconResName; } - // TODO: Omit these by default and add a new clone flag. - mChooserIntentFilters = source.mChooserIntentFilters; - mChooserComponentNames = source.mChooserComponentNames; - mChooserExtras = source.mChooserExtras; } else { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; @@ -565,25 +504,6 @@ public final class ShortcutInfo implements Parcelable { } /** - * Whether the shortcut has any intentFilter matching the passed in one. - * @hide - */ - @VisibleForTesting - public boolean hasMatchingFilter(ContentResolver resolver, Intent intent) { - if (mChooserIntentFilters == null) { - return false; - } - for (IntentFilter filter : mChooserIntentFilters) { - int match = filter.match(resolver, intent, false, TAG); - if (match > 0) { - return true; - } - } - return false; - } - - - /** * Extract the entry name from a fully-donated resource name. * e.g. "com.android.app1:drawable/icon1" -> "icon1" * @hide @@ -766,15 +686,6 @@ public final class ShortcutInfo implements Parcelable { if (source.mExtras != null) { mExtras = source.mExtras; } - if (source.mChooserExtras != null) { - mChooserExtras = source.mChooserExtras; - } - if (source.mChooserIntentFilters != null) { - mChooserIntentFilters = source.mChooserIntentFilters; - } - if (source.mChooserComponentNames != null) { - mChooserComponentNames = source.mChooserComponentNames; - } } /** @@ -836,12 +747,6 @@ public final class ShortcutInfo implements Parcelable { private PersistableBundle mExtras; - private PersistableBundle mChooserExtras; - - private List<IntentFilter> mChooserIntentFilters; - - private List<ComponentName> mChooserComponentNames; - /** * Old style constructor. * @hide @@ -1127,37 +1032,17 @@ public final class ShortcutInfo implements Parcelable { return this; } - /** - * Extras that can be added which will be added to the Intent used to launch the app if - * launched from a chooser context. - */ + /** @deprecated punted, don't use. */ + @Deprecated @NonNull public Builder setChooserExtras(@NonNull PersistableBundle extras) { - mChooserExtras = extras; return this; } - /** - * IntentFilters and the components that should resolve a match for a given chooser target. - * If multiple matches are found, the component corresponding to the closest match will be - * used. - * - * @param filter IntendFilter that if matched will have the intent forwarded to the given - * component - * @param name The component that an intent that passes this filter will resolve to. - */ + /** @deprecated punted, don't use. */ + @Deprecated public Builder addChooserIntentFilter(@NonNull IntentFilter filter, @NonNull ComponentName name) { - Preconditions.checkNotNull(filter, "intent filter cannot be null"); - Preconditions.checkNotNull(name, "component name cannot be null"); - - if (mChooserIntentFilters == null || mChooserComponentNames == null) { - mChooserIntentFilters = new ArrayList<>(); - mChooserComponentNames = new ArrayList<>(); - } - - mChooserIntentFilters.add(filter); - mChooserComponentNames.add(name); return this; } @@ -1361,28 +1246,25 @@ public final class ShortcutInfo implements Parcelable { return mIntentPersistableExtrases; } - /** - * Retrieve the extras that will be added in to any intent launched through the chooser. - */ + /** @deprecated punted, don't use. */ + @Deprecated @NonNull public PersistableBundle getChooserExtras() { - return mChooserExtras; + return new PersistableBundle(); } - /** - * Retrieve the list of intent filters for chooser targets. - */ + /** @deprecated punted, don't use. */ + @Deprecated @NonNull public IntentFilter[] getChooserIntentFilters() { - return mChooserIntentFilters; + return new IntentFilter[0]; } - /** - * Retrieve the list of component names corresponding to the above intent filters. - */ + /** @deprecated punted, don't use. */ + @Deprecated @NonNull public ComponentName[] getChooserComponentNames() { - return mChooserComponentNames; + return new ComponentName[0]; } /** @@ -1506,9 +1388,10 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_PINNED); } - /** Return whether a shortcut can be shown in the chooser. */ + /** @deprecated punted, don't use. */ + @Deprecated public boolean isChooser() { - return hasFlags(FLAG_CHOOSER); + return false; } /** @@ -1539,14 +1422,6 @@ public final class ShortcutInfo implements Parcelable { return isPinned() && !(isDynamic() || isManifestShortcut()); } - /** - * @return true if pinned but neither static nor dynamic. - * @hide - */ - public boolean isDynamicOrChooser() { - return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_CHOOSER); - } - /** @hide */ public boolean isOriginallyFromManifest() { return hasFlags(FLAG_IMMUTABLE); @@ -1829,19 +1704,6 @@ public final class ShortcutInfo implements Parcelable { mCategories.add(source.readString().intern()); } } - - // We put a placeholder empty array in to keep the parcelable order, but can do away with - // them at this point if they're empty. - mChooserComponentNames = source.readParcelableArray(cl, ComponentName.class); - if (mChooserComponentNames.length == 0) { - mChooserComponentNames = null; - } - - mChooserIntentFilters = source.readParcelableArray(cl, IntentFilter.class); - if (mChooserIntentFilters.length == 0) { - mChooserIntentFilters = null; - } - mChooserExtras = source.readPersistableBundle(cl); } @Override @@ -1888,17 +1750,6 @@ public final class ShortcutInfo implements Parcelable { } else { dest.writeInt(0); } - if (mChooserComponentNames != null) { - dest.writeParcelableArray(mChooserComponentNames, flags); - } else { - dest.writeParcelableArray(new ComponentName[0], flags); - } - if (mChooserIntentFilters != null) { - dest.writeParcelableArray(mChooserIntentFilters, flags); - } else { - dest.writeParcelableArray(new IntentFilter[0], flags); - } - dest.writePersistableBundle(mChooserExtras); } public static final Creator<ShortcutInfo> CREATOR = diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 3de19d19bfc3..7b7d8ae42528 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -45,8 +45,8 @@ public abstract class ShortcutServiceInternal { getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, @Nullable Intent intent, - @ShortcutQuery.QueryFlags int flags, int userId); + @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags, + int userId); public abstract boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 99fbee1ea3cc..c8353c9bffb9 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -16,6 +16,11 @@ package android.content.res; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.DisplayInfo; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -293,6 +298,16 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int screenLayout; + /** + * @hide + * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of + * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at + * the display level. Lower levels can override these values to provide custom bounds to enforce + * features such as a max aspect ratio. + * TODO(b/36812336): Move appBounds out of {@link Configuration}. + */ + public Rect appBounds; + /** @hide */ static public int resetScreenLayout(int curLayout) { return (curLayout&~(SCREENLAYOUT_LONG_MASK | SCREENLAYOUT_SIZE_MASK @@ -882,6 +897,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = o.compatScreenWidthDp; compatScreenHeightDp = o.compatScreenHeightDp; compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp; + setAppBounds(o.appBounds); assetsSeq = o.assetsSeq; seq = o.seq; } @@ -1032,6 +1048,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration case NAVIGATIONHIDDEN_YES: sb.append("/h"); break; default: sb.append("/"); sb.append(navigationHidden); break; } + if (appBounds != null) { + sb.append(" appBounds="); sb.append(appBounds); + } if (assetsSeq != 0) { sb.append(" as.").append(assetsSeq); } @@ -1066,6 +1085,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; densityDpi = DENSITY_DPI_UNDEFINED; assetsSeq = ASSETS_SEQ_UNDEFINED; + appBounds = null; seq = 0; } @@ -1253,6 +1273,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp; } + if (delta.appBounds != null && !delta.appBounds.equals(appBounds)) { + changed |= ActivityInfo.CONFIG_SCREEN_SIZE; + setAppBounds(delta.appBounds); + } if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED) { changed |= ActivityInfo.CONFIG_ASSETS_PATHS; assetsSeq = delta.assetsSeq; @@ -1399,6 +1423,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_ASSETS_PATHS; } + // Make sure that one of the values is not null and that they are not equal. + if ((compareUndefined || delta.appBounds != null) + && appBounds != delta.appBounds + && (appBounds == null || !appBounds.equals(delta.appBounds))) { + changed |= ActivityInfo.CONFIG_SCREEN_SIZE; + } + return changed; } @@ -1494,6 +1525,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenWidthDp); dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); + dest.writeValue(appBounds); dest.writeInt(assetsSeq); dest.writeInt(seq); } @@ -1529,6 +1561,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = source.readInt(); compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); + appBounds = (Rect) source.readValue(null); assetsSeq = source.readInt(); seq = source.readInt(); } @@ -1706,6 +1739,33 @@ public final class Configuration implements Parcelable, Comparable<Configuration /** * @hide * + * Helper method for setting the app bounds. + */ + public void setAppBounds(Rect rect) { + if (rect == null) { + appBounds = null; + return; + } + + setAppBounds(rect.left, rect.top, rect.right, rect.bottom); + } + + /** + * @hide + * + * Helper method for setting the app bounds. + */ + public void setAppBounds(int left, int top, int right, int bottom) { + if (appBounds == null) { + appBounds = new Rect(); + } + + appBounds.set(left, top, right, bottom); + } + + /** + * @hide + * * Clears the locale without changing layout direction. */ public void clearLocales() { @@ -2212,6 +2272,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration private static final String XML_ATTR_SCREEN_HEIGHT = "height"; private static final String XML_ATTR_SMALLEST_WIDTH = "sw"; private static final String XML_ATTR_DENSITY = "density"; + private static final String XML_ATTR_APP_BOUNDS = "app_bounds"; /** * Reads the attributes corresponding to Configuration member fields from the Xml parser. @@ -2261,6 +2322,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration SMALLEST_SCREEN_WIDTH_DP_UNDEFINED); configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY, DENSITY_DPI_UNDEFINED); + configOut.appBounds = + Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS)); // For persistence, we don't care about assetsSeq, so do not read it out. } @@ -2332,6 +2395,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi); } + if (config.appBounds != null) { + XmlUtils.writeStringAttribute(xml, XML_ATTR_APP_BOUNDS, + config.appBounds.flattenToString()); + } + // For persistence, we do not care about assetsSeq, so do not write it out. } } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index b27600800cc6..e845359a35c4 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -102,6 +102,16 @@ public abstract class DisplayManagerInternal { int displayId, DisplayInfo info); /** + * Get current display info without override from WindowManager. + * Current implementation of LogicalDisplay#getDisplayInfoLocked() always returns display info + * with overrides from WM if set. This method can be used for getting real display size without + * overrides to determine if real changes to display metrics happened. + * @param displayId Id of the target display. + * @param outInfo {@link DisplayInfo} to fill. + */ + public abstract void getNonOverrideDisplayInfo(int displayId, DisplayInfo outInfo); + + /** * Called by the window manager to perform traversals while holding a * surface flinger transaction. */ diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index badb344aabad..239a2df5e1c3 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -60,9 +60,11 @@ public class UsbRequest { // Prevent the connection from being finalized private UsbDeviceConnection mConnection; - /** Whether this buffer was {@link #enqueue enqueued (new behavior)} or {@link #queue queued - * (deprecared behavior)}. */ - private boolean mIsUsingEnqueue; + /** + * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or + * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. + */ + private boolean mIsUsingNewQueue; /** Temporary buffer than might be used while buffer is enqueued */ private ByteBuffer mTempBuffer; @@ -172,7 +174,7 @@ public class UsbRequest { * * @return true if the queueing operation succeeded * - * @deprecated Use {@link #enqueue(ByteBuffer)} instead. + * @deprecated Use {@link #queue(ByteBuffer)} instead. */ @Deprecated public boolean queue(ByteBuffer buffer, int length) { @@ -219,23 +221,23 @@ public class UsbRequest { * * @return true if the queueing operation succeeded */ - public boolean enqueue(@Nullable ByteBuffer buffer) { + public boolean queue(@Nullable ByteBuffer buffer) { // Request need to be initialized Preconditions.checkState(mNativeContext != 0, "request is not initialized"); - // Request can not be currently enqueued - Preconditions.checkState(!mIsUsingEnqueue, "request is currently enqueued"); + // Request can not be currently queued + Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); - boolean wasEnqueued; + boolean wasQueued; synchronized (mLock) { mBuffer = buffer; if (buffer == null) { // Null buffers enqueue empty USB requests which is supported - mIsUsingEnqueue = true; - wasEnqueued = native_enqueue(null, 0, 0); + mIsUsingNewQueue = true; + wasQueued = native_queue(null, 0, 0); } else { // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, @@ -260,18 +262,18 @@ public class UsbRequest { buffer = mTempBuffer; } - mIsUsingEnqueue = true; - wasEnqueued = native_enqueue(buffer, buffer.position(), buffer.remaining()); + mIsUsingNewQueue = true; + wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); } } - if (!wasEnqueued) { - mIsUsingEnqueue = false; + if (!wasQueued) { + mIsUsingNewQueue = false; mTempBuffer = null; mBuffer = null; } - return wasEnqueued; + return wasQueued; } /* package */ void dequeue() { @@ -279,9 +281,9 @@ public class UsbRequest { int bytesTransferred; synchronized (mLock) { - if (mIsUsingEnqueue) { + if (mIsUsingNewQueue) { bytesTransferred = native_dequeue_direct(); - mIsUsingEnqueue = false; + mIsUsingNewQueue = false; if (mBuffer == null) { // Nothing to do @@ -332,7 +334,7 @@ public class UsbRequest { private native boolean native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval); private native void native_close(); - private native boolean native_enqueue(ByteBuffer buffer, int offset, int length); + private native boolean native_queue(ByteBuffer buffer, int offset, int length); private native boolean native_queue_array(byte[] buffer, int length, boolean out); private native int native_dequeue_array(byte[] buffer, int length, boolean out); private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 495340dc7222..63bbd96bd01d 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -38,9 +38,6 @@ interface INetworkPolicyManager { boolean isUidForeground(int uid); - /** Higher priority listener before general event dispatch */ - void setConnectivityListener(INetworkPolicyListener listener); - void registerListener(INetworkPolicyListener listener); void unregisterListener(INetworkPolicyListener listener); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e2100bd43898..660d53a91b99 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10324,6 +10324,10 @@ public final class Settings { INSTANT_APP_SETTINGS.add(DEVELOPMENT_FORCE_RTL); INSTANT_APP_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES); INSTANT_APP_SETTINGS.add(AIRPLANE_MODE_ON); + INSTANT_APP_SETTINGS.add(WINDOW_ANIMATION_SCALE); + INSTANT_APP_SETTINGS.add(TRANSITION_ANIMATION_SCALE); + INSTANT_APP_SETTINGS.add(ANIMATOR_DURATION_SCALE); + INSTANT_APP_SETTINGS.add(DEBUG_VIEW_ATTRIBUTES); } /** diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index 8e0103078a42..56b267fb2c9d 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -19,11 +19,13 @@ import android.Manifest; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Dialog; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Handler; import android.os.IBinder; @@ -34,6 +36,8 @@ import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.WindowManager; +import com.android.internal.R; + /** * A TileService provides the user a tile that can be added to Quick Settings. * Quick Settings is a space provided that allows the user to change settings and @@ -425,6 +429,15 @@ public class TileService extends Service { } /** + * @return True if the device supports quick settings and its assocated APIs. + * @hide + */ + @TestApi + public static boolean isQuickSettingsSupported() { + return Resources.getSystem().getBoolean(R.bool.config_quickSettingsSupported); + } + + /** * Requests that a tile be put in the listening state so it can send an update. * * This method is only applicable to tiles that have {@link #META_DATA_ACTIVE_TILE} defined diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 5494377ceebd..6dedbde01995 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -50,7 +50,7 @@ import java.util.Arrays; * <li>The real display area specifies the part of the display that contains content * including the system decorations. Even so, the real display area may be smaller than the * physical size of the display if the window manager is emulating a smaller display - * using (adb shell am display-size). Use the following methods to query the + * using (adb shell wm size). Use the following methods to query the * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li> * </ul> * </p><p> @@ -947,7 +947,7 @@ public final class Display { * The size is adjusted based on the current rotation of the display. * </p><p> * The real size may be smaller than the physical size of the screen when the - * window manager is emulating a smaller display (using adb shell am display-size). + * window manager is emulating a smaller display (using adb shell wm size). * </p> * * @param outSize Set to the real size of the display. diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 3d11dcbf14bf..0cec496fa264 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -562,12 +562,10 @@ public final class DisplayInfo implements Parcelable { outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi; outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi; - width = (configuration != null - && configuration.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) - ? (int)((configuration.screenWidthDp * outMetrics.density) + 0.5f) : width; - height = (configuration != null - && configuration.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) - ? (int)((configuration.screenHeightDp * outMetrics.density) + 0.5f) : height; + width = configuration != null && configuration.appBounds != null + ? configuration.appBounds.width() : width; + height = configuration != null && configuration.appBounds != null + ? configuration.appBounds.height() : height; outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width; outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height; diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index ae1ee42c8720..d25e5f04fad2 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -118,7 +118,7 @@ public class FocusFinder { * @return the "effective" root of {@param focused} */ private ViewGroup getEffectiveRoot(ViewGroup root, View focused) { - if (focused == null) { + if (focused == null || focused == root) { return root; } ViewParent effective = focused.getParent(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 884283dee789..b12a7676dd7f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -25523,7 +25523,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p> * The tooltip will be displayed: * <ul> - * <li>On long click, unless is not handled otherwise (by OnLongClickListener or a context + * <li>On long click, unless it is handled otherwise (by OnLongClickListener or a context * menu). </li> * <li>On hover, after a brief delay since the pointer has stopped moving </li> * </ul> diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 79b0420a77e3..958d76109a9e 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1018,9 +1018,20 @@ public final class InputMethodManager { } } - /** @hide */ + /** + * This method is still kept for a while until android.support.v7.widget.SearchView ver. 26.0 + * is publicly released because previous implementations of that class had relied on this method + * via reflection. + * + * @deprecated This is a hidden API. You should never use this. + * @hide + */ + @Deprecated public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { try { + Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be removed " + + "soon. If you are using android.support.v7.widget.SearchView, please update " + + "to version 26.0 or newer version."); mService.showSoftInput(mClient, flags, resultReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c235ebd4720d..df65659dface 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -23,21 +23,15 @@ import android.app.usage.UsageStatsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.LabeledIntent; -import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; -import android.content.pm.ShortcutInfo; -import android.content.pm.ShortcutManager; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -362,7 +356,6 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets)); } mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); - mChooserRowAdapter.updateRowScales(); mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { @@ -849,9 +842,7 @@ public class ChooserActivity extends ResolverActivity { return false; } intent.setComponent(mChooserTarget.getComponentName()); - if (mChooserTarget.getIntentExtras() != null) { - intent.putExtras(mChooserTarget.getIntentExtras()); - } + intent.putExtras(mChooserTarget.getIntentExtras()); // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package @@ -934,8 +925,6 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_SERVICE_TARGETS = 8; private static final int MAX_TARGETS_PER_SERVICE = 4; - private boolean mAreChooserShortcutsRetrieved; - private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); private final List<TargetInfo> mCallerTargets = new ArrayList<>(); private boolean mShowServiceTargets; @@ -1027,21 +1016,6 @@ public class ChooserActivity extends ResolverActivity { if (mServiceTargets != null) { pruneServiceTargets(); } - - if (DEBUG) Log.d(TAG, "Adding pushed chooser targets"); - - if (!mAreChooserShortcutsRetrieved) { - LauncherApps launcherApps = getLauncherApps(); - LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); - query.setIntent(getTargetIntent()); - query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_CHOOSER); - List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, - android.os.Process.myUserHandle()); - if (DEBUG) Log.d(TAG, "Adding " + shortcuts.size() + " chooser shortcuts"); - addShortcuts(shortcuts); - mAreChooserShortcutsRetrieved = true; - } - if (DEBUG) Log.d(TAG, "List built querying services"); queryTargetServices(this); } @@ -1067,7 +1041,6 @@ public class ChooserActivity extends ResolverActivity { public int getServiceTargetCount() { if (!mShowServiceTargets) { - if (DEBUG) Log.d("TAG", "Hiding service targets"); return 0; } return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); @@ -1159,71 +1132,6 @@ public class ChooserActivity extends ResolverActivity { notifyDataSetChanged(); } - // TODO: Pushed targets need to be scored correctly - public void addShortcuts(List<ShortcutInfo> infos) { - for (ShortcutInfo info : infos) { - List<ChooserTarget> newTargets = new ArrayList<>(); - final ComponentName cn = info.getActivity(); - ActivityInfo ai; - ResolveInfo ri = new ResolveInfo(); - if (cn != null) { - try { - ai = getPackageManager().getActivityInfo(cn, 0); - ri.activityInfo = ai; - UserManager userManager = - (UserManager) getSystemService(Context.USER_SERVICE); - ri.iconResourceId = ai.icon; - ri.labelRes = ai.labelRes; - ri.resolvePackageName = ai.packageName; - ri.activityInfo.applicationInfo = new ApplicationInfo( - ri.activityInfo.applicationInfo); - ri.activityInfo.applicationInfo = ai.applicationInfo; - ri.activityInfo.applicationInfo.uid = getUserId(); - } catch (PackageManager.NameNotFoundException ignored) { - if (DEBUG) Log.d(TAG, "Package not found, skipping this shortcut"); - continue; - } - } - - DisplayResolveInfo resolveInfo = new DisplayResolveInfo(getTargetIntent(), - ri, - info.getShortLabel(), - info.getLongLabel(), - getTargetIntent()); - - int bestMatch = 0; - ComponentName bestComponent = null; - for (int i = 0; i < info.getChooserIntentFilters().length; i++) { - int newMatch = info.getChooserIntentFilters()[i] - .match(getContentResolver(), getTargetIntent(), false, TAG); - if (DEBUG) Log.d(TAG, "A match was found with value: " + newMatch); - if (newMatch > bestMatch) { - bestMatch = newMatch; - bestComponent = info.getChooserComponentNames()[i]; - } - } - if (bestMatch == 0) { - Log.e(TAG, "Unexpectedly, no match was found for the provided chooser intent"); - return; - } - - Bundle extrasToAdd = - info.getChooserExtras() == null ? null: new Bundle(info.getChooserExtras()); - if (DEBUG) Log.d(TAG, "Adding service target " + info.getShortLabel()); - newTargets.add(new ChooserTarget( - info.getShortLabel(), - info.getIcon(), - 1, - bestComponent, - extrasToAdd)); - addServiceResults(resolveInfo, newTargets); - } - if (mChooserRowAdapter != null) { - mChooserRowAdapter.updateRowScales(); - } - setShowServiceTargets(true); - } - /** * Set to true to reveal all service targets at once. */ @@ -1338,7 +1246,37 @@ public class ChooserActivity extends ResolverActivity { @Override public void onChanged() { super.onChanged(); - updateRowScales(); + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + } + + // Start the animations in a separate loop. + // The process of starting animations will result in + // binding views to set up initial values, and we must + // have ALL of the new RowScale objects created above before + // we get started. + for (int i = oldRCount; i < rcount; i++) { + mServiceTargetScale[i].startAnimation(); + } + } + notifyDataSetChanged(); } @@ -1355,40 +1293,6 @@ public class ChooserActivity extends ResolverActivity { }); } - void updateRowScales() { - final int rcount = getServiceTargetRowCount(); - if (mServiceTargetScale == null - || mServiceTargetScale.length != rcount) { - if (DEBUG) Log.d(TAG, "Row scales need adjusting to " + rcount + " rows."); - RowScale[] old = mServiceTargetScale; - int oldRCount = old != null ? old.length : 0; - mServiceTargetScale = new RowScale[rcount]; - if (old != null && rcount > 0) { - System.arraycopy(old, 0, mServiceTargetScale, 0, - Math.min(old.length, rcount)); - } - - for (int i = rcount; i < oldRCount; i++) { - old[i].cancelAnimation(); - } - - for (int i = oldRCount; i < rcount; i++) { - final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) - .setInterpolator(mInterpolator); - mServiceTargetScale[i] = rs; - } - - // Start the animations in a separate loop. - // The process of starting animations will result in - // binding views to set up initial values, and we must - // have ALL of the new RowScale objects created above before - // we get started. - for (int i = oldRCount; i < rcount; i++) { - mServiceTargetScale[i].startAnimation(); - } - } - } - private float getRowScale(int rowPosition) { final int start = getCallerTargetRowCount(); final int end = start + getServiceTargetRowCount(); @@ -1659,10 +1563,6 @@ public class ChooserActivity extends ResolverActivity { } } - public LauncherApps getLauncherApps() { - return (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE); - } - static class ServiceResultInfo { public final DisplayResolveInfo originalTarget; public final List<ChooserTarget> resultTargets; diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 1abb59b006dd..a70209c705c0 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -74,6 +74,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame private final Rect mOldStableInsets = new Rect(); private final Rect mSystemInsets = new Rect(); private final Rect mStableInsets = new Rect(); + private final Rect mTmpRect = new Rect(); public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable, @@ -370,12 +371,6 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height); mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height); final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top); - final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom, - systemInsets.bottom); - final int rightInset = DecorView.getColorViewRightInset(stableInsets.right, - systemInsets.right); - final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left, - systemInsets.left); if (mStatusBarColor != null) { mStatusBarColor.setBounds(0, 0, left + width, topInset); mStatusBarColor.draw(canvas); @@ -385,14 +380,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame // don't want the navigation bar background be moving around when resizing in docked mode. // However, we need it for the transitions into/out of docked mode. if (mNavigationBarColor != null && fullscreen) { - final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset); - if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) { - mNavigationBarColor.setBounds(width - size, 0, width, height); - } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) { - mNavigationBarColor.setBounds(0, 0, size, height); - } else { - mNavigationBarColor.setBounds(0, height - size, width, height); - } + DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect); + mNavigationBarColor.setBounds(mTmpRect); mNavigationBarColor.draw(canvas); } mSystemBarBackgroundNode.end(canvas); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index a8e16c96acfa..653796dc39e7 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -119,6 +119,21 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // The height of a window which has not in DIP. private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5; + public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES = + new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, + Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, + Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.statusBarBackground, + FLAG_FULLSCREEN); + + public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES = + new ColorViewAttributes( + SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, + Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, + Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.navigationBarBackground, + 0 /* hideWindowFlag */); + // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer // size calculation takes the shadow size into account. We set the elevation currently // to max until the first layout command has been executed. @@ -162,18 +177,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // View added at runtime to draw under the navigation bar area private View mNavigationGuard; - private final ColorViewState mStatusColorViewState = new ColorViewState( - SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, - Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, - Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, - com.android.internal.R.id.statusBarBackground, - FLAG_FULLSCREEN); - private final ColorViewState mNavigationColorViewState = new ColorViewState( - SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, - Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, - Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, - com.android.internal.R.id.navigationBarBackground, - 0 /* hideWindowFlag */); + private final ColorViewState mStatusColorViewState = + new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES); + private final ColorViewState mNavigationColorViewState = + new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES); private final Interpolator mShowInterpolator; private final Interpolator mHideInterpolator; @@ -983,35 +990,50 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return false; } - static int getColorViewTopInset(int stableTop, int systemTop) { + public static int getColorViewTopInset(int stableTop, int systemTop) { return Math.min(stableTop, systemTop); } - static int getColorViewBottomInset(int stableBottom, int systemBottom) { + public static int getColorViewBottomInset(int stableBottom, int systemBottom) { return Math.min(stableBottom, systemBottom); } - static int getColorViewRightInset(int stableRight, int systemRight) { + public static int getColorViewRightInset(int stableRight, int systemRight) { return Math.min(stableRight, systemRight); } - static int getColorViewLeftInset(int stableLeft, int systemLeft) { + public static int getColorViewLeftInset(int stableLeft, int systemLeft) { return Math.min(stableLeft, systemLeft); } - static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { + public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { return bottomInset == 0 && rightInset > 0; } - static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { + public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { return bottomInset == 0 && leftInset > 0; } - static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { + public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset; } + public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets, + Rect contentInsets, Rect outRect) { + final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom); + final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left); + final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right); + final int size = getNavBarSize(bottomInset, rightInset, leftInset); + if (isNavBarToRightEdge(bottomInset, rightInset)) { + outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight); + } else if (isNavBarToLeftEdge(bottomInset, leftInset)) { + outRect.set(0, 0, size, canvasHeight); + } else { + outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight); + } + } + WindowInsets updateColorViews(WindowInsets insets, boolean animate) { WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); @@ -1131,9 +1153,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } private int calculateStatusBarColor() { - int flags = mWindow.getAttributes().flags; - return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor - : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor + return calculateStatusBarColor(mWindow.getAttributes().flags, + mSemiTransparentStatusBarColor, mWindow.mStatusBarColor); + } + + public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor, + int statusBarColor) { + return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor + : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor : Color.BLACK; } @@ -1160,13 +1187,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force) { - state.present = (sysUiVis & state.systemUiHideFlag) == 0 - && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0 - && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || force); - boolean show = state.present - && (color & Color.BLACK) != 0 - && ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force); + state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force); + boolean show = state.attributes.isVisible(state.present, color, + mWindow.getAttributes().flags, force); boolean showView = show && !isResizing() && size > 0; boolean visibilityChanged = false; @@ -1175,15 +1198,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size; int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT; int resolvedGravity = verticalBar - ? (seascape ? state.seascapeGravity : state.horizontalGravity) - : state.verticalGravity; + ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity) + : state.attributes.verticalGravity; if (view == null) { if (showView) { state.view = view = new View(mContext); view.setBackgroundColor(color); - view.setTransitionName(state.transitionName); - view.setId(state.id); + view.setTransitionName(state.attributes.transitionName); + view.setId(state.attributes.id); visibilityChanged = true; view.setVisibility(INVISIBLE); state.targetVisibility = VISIBLE; @@ -2269,6 +2292,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind boolean visible; int color; + final ColorViewAttributes attributes; + + ColorViewState(ColorViewAttributes attributes) { + this.attributes = attributes; + } + } + + public static class ColorViewAttributes { + final int id; final int systemUiHideFlag; final int translucentFlag; @@ -2278,9 +2310,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final String transitionName; final int hideWindowFlag; - ColorViewState(int systemUiHideFlag, - int translucentFlag, int verticalGravity, int horizontalGravity, - int seascapeGravity, String transitionName, int id, int hideWindowFlag) { + private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity, + int horizontalGravity, int seascapeGravity, String transitionName, int id, + int hideWindowFlag) { this.id = id; this.systemUiHideFlag = systemUiHideFlag; this.translucentFlag = translucentFlag; @@ -2290,6 +2322,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind this.transitionName = transitionName; this.hideWindowFlag = hideWindowFlag; } + + public boolean isPresent(int sysUiVis, int windowFlags, boolean force) { + return (sysUiVis & systemUiHideFlag) == 0 + && (windowFlags & hideWindowFlag) == 0 + && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + || force); + } + + public boolean isVisible(boolean present, int color, int windowFlags, boolean force) { + return present + && (color & Color.BLACK) != 0 + && ((windowFlags & translucentFlag) == 0 || force); + } + + public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) { + final boolean present = isPresent(sysUiVis, windowFlags, force); + return isVisible(present, color, windowFlags, force); + } } /** diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp index 4b7e0dd562b2..01fe078f6f16 100644 --- a/core/jni/android_hardware_UsbRequest.cpp +++ b/core/jni/android_hardware_UsbRequest.cpp @@ -167,7 +167,7 @@ android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz, } static jboolean -android_hardware_UsbRequest_enqueue(JNIEnv *env, jobject thiz, jobject buffer, jint offset, +android_hardware_UsbRequest_queue(JNIEnv *env, jobject thiz, jobject buffer, jint offset, jint length) { struct usb_request* request = get_request_from_object(env, thiz); @@ -226,8 +226,8 @@ static const JNINativeMethod method_table[] = { {"native_init", "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z", (void *)android_hardware_UsbRequest_init}, {"native_close", "()V", (void *)android_hardware_UsbRequest_close}, - {"native_enqueue", "(Ljava/nio/ByteBuffer;II)Z", - (void *)android_hardware_UsbRequest_enqueue}, + {"native_queue", "(Ljava/nio/ByteBuffer;II)Z", + (void *)android_hardware_UsbRequest_queue}, {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array}, {"native_dequeue_array", "([BIZ)I", (void *)android_hardware_UsbRequest_dequeue_array}, {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z", diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 713287e4cd7a..5839fd50d79a 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -101,17 +101,7 @@ sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { return sur; } -jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, - const sp<IGraphicBufferProducer>& bufferProducer) { - if (bufferProducer == NULL) { - return NULL; - } - - sp<Surface> surface(new Surface(bufferProducer, true)); - if (surface == NULL) { - return NULL; - } - +jobject android_view_Surface_createFromSurface(JNIEnv* env, const sp<Surface>& surface) { jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz, gSurfaceClassInfo.ctor, (jlong)surface.get()); if (surfaceObj == NULL) { @@ -126,6 +116,16 @@ jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, return surfaceObj; } +jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, + const sp<IGraphicBufferProducer>& bufferProducer) { + if (bufferProducer == NULL) { + return NULL; + } + + sp<Surface> surface(new Surface(bufferProducer, true)); + return android_view_Surface_createFromSurface(env, surface); +} + int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { switch(f) { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 0ab27f2e0f8d..b95258bca49c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -346,6 +346,11 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, return false; } + // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE. + if (mount_mode == MOUNT_EXTERNAL_NONE) { + return true; + } + if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno)); diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h index 3f1bdff81aef..2641ab8f0337 100644 --- a/core/jni/include/android_runtime/android_view_Surface.h +++ b/core/jni/include/android_runtime/android_view_Surface.h @@ -69,6 +69,10 @@ extern bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj); /* Gets the underlying Surface from a Surface Java object. */ extern sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj); +/* Creates a Surface from an android::Surface. */ +extern jobject android_view_Surface_createFromSurface(JNIEnv* env, + const sp<Surface>& surface); + /* Creates a Surface from an IGraphicBufferProducer. */ extern jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 362794e81c55..e7a447c54aef 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -798,13 +798,13 @@ android:description="@string/permdesc_readPhoneState" android:protectionLevel="dangerous" /> - <!-- Allows read access to the device's phone number. This is a subset of the capabilities + <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities granted by {@link #READ_PHONE_STATE} but is exposed to ephemeral applications. <p>Protection level: dangerous--> - <permission android:name="android.permission.READ_PHONE_NUMBER" + <permission android:name="android.permission.READ_PHONE_NUMBERS" android:permissionGroup="android.permission-group.PHONE" android:label="@string/permlab_readPhoneNumber" - android:description="@string/permdesc_readPhoneNumber" + android:description="@string/permdesc_readPhoneNumbers" android:protectionLevel="dangerous|ephemeral" /> <!-- Allows an application to initiate a phone call without going through @@ -3346,7 +3346,8 @@ android:documentLaunchMode="never" android:relinquishTaskIdentity="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> <intent-filter> <action android:name="android.intent.action.CHOOSER" /> <category android:name="android.intent.category.DEFAULT" /> @@ -3360,7 +3361,8 @@ android:documentLaunchMode="never" android:relinquishTaskIdentity="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> <intent-filter> <action android:name="android.intent.action.CHOOSE_ACCESSIBILITY_BUTTON" /> <category android:name="android.intent.category.DEFAULT" /> @@ -3421,7 +3423,8 @@ android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.ChooseTypeAndAccountActivity" @@ -3429,14 +3432,16 @@ android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.ChooseAccountTypeActivity" android:excludeFromRecents="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.CantAddAccountActivity" @@ -3450,7 +3455,8 @@ android:excludeFromRecents="true" android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.content.SyncActivityTooManyDeletes" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 554f50c23daa..ee73b6983888 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8566,6 +8566,11 @@ <!-- @hide From Theme.colorBackground, used for the TaskDescription background color. --> <attr name="colorBackground" /> + <!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. --> + <attr name="statusBarColor"/> + <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar + color. --> + <attr name="navigationBarColor"/> </declare-styleable> <declare-styleable name="Shortcut"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bcd8a8aeb7f7..85ecaff26b21 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2819,4 +2819,7 @@ density will be scaled accordingly to maintain aspect ratio. A value of 0 indicates no constraint will be enforced. --> <integer name="config_maxUiWidth">0</integer> + + <!-- Whether the device supports quick settings and its associated APIs --> + <bool name="config_quickSettingsSupported">true</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4afa8dcdfb4a..831cf89e0e97 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Suffix added to a number to signify size in bytes. --> <string name="byteShort">B</string> - <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). - If you retain the Latin script for the localization, please use the lowercase - 'k', as it signifies 1000 bytes as opposed to 1024 bytes. --> - <string name="kilobyteShort">kB</string> + <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). --> + <string name="kilobyteShort">KB</string> <!-- Suffix added to a number to signify size in megabytes. --> <string name="megabyteShort">MB</string> <!-- Suffix added to a number to signify size in gigabytes. --> @@ -1094,9 +1092,9 @@ order to improve the calling experience.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_readPhoneNumber">read phone number</string> + <string name="permlab_readPhoneNumbers">read phone numbers</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_readPhoneNumber">Allows the app to access the phone number of the device.</string> + <string name="permdesc_readPhoneNumbers">Allows the app to access the phone numbers of the device.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_wakeLock" product="tablet">prevent tablet from sleeping</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index dfd18e77a264..ae05a69da20d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2955,4 +2955,5 @@ <java-symbol type="string" name="etws_primary_default_message_test" /> <java-symbol type="string" name="etws_primary_default_message_others" /> + <java-symbol type="bool" name="config_quickSettingsSupported" /> </resources> diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 3dfecc646169..1080a9fcfe71 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -16,17 +16,6 @@ package com.android.internal.app; -import android.app.Instrumentation; -import android.content.ComponentName; -import android.content.IntentFilter; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.LauncherApps; -import android.content.pm.PackageManager; -import android.content.pm.ShortcutInfo; -import android.content.pm.ShortcutManager; -import android.graphics.drawable.Icon; -import android.os.SystemClock; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; @@ -59,31 +48,25 @@ import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.isA; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; /** * Chooser activity instrumentation tests */ @RunWith(AndroidJUnit4.class) public class ChooserActivityTest { - private Instrumentation instrumentation; - - @Before - public void setUp() { - instrumentation = InstrumentationRegistry.getInstrumentation(); - sOverrides.reset(); - } - @Rule public ActivityTestRule<ChooserWrapperActivity> mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); + @Before + public void cleanOverrideData() { + sOverrides.reset(); + } + @Test public void customTitle() throws InterruptedException { Intent sendIntent = createSendImageIntent(); @@ -252,6 +235,7 @@ public class ChooserActivityTest { chosen[0] = targetInfo.getResolveInfo(); return true; }; + // Make a stable copy of the components as the original list may be modified List<ResolvedComponentInfo> stableCopy = createResolvedComponentsForTestWithOtherProfile(2); @@ -340,32 +324,6 @@ public class ChooserActivityTest { assertThat(chosen[0], is(toChoose)); } - public void pushedChooserTarget() { - ResolveInfo[] chosen = new ResolveInfo[1]; - sOverrides.onSafelyStartCallback = targetInfo -> { - chosen[0] = targetInfo.getResolveInfo(); - return true; - }; - - setChooserShortcuts(1); - List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); - when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); - - Intent sendIntent = createSendImageIntent(); - final ChooserWrapperActivity activity = mActivityRule - .launchActivity(Intent.createChooser(sendIntent, null)); - - waitForIdle(); - - onView(withText("short chooser label 0")) - .perform(click()); - waitForIdle(); - assertThat(chosen[0].resolvePackageName, - is(ResolverDataProvider.createActivityInfo(0).packageName)); - } - private Intent createSendImageIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); @@ -413,48 +371,4 @@ public class ChooserActivityTest { } return packageStats.mChooserCounts.get(action).getOrDefault(annotation, 0); } - - private void setChooserShortcuts(int numShortcuts) { - ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); - for (int i = 0; i < numShortcuts; i++) { - shortcuts.add(makeShortcut(i)); - } - when(sOverrides.launcherApps.getShortcuts( - Mockito.isA(LauncherApps.ShortcutQuery.class), - Mockito.eq(UserHandle.SYSTEM))) - .thenReturn(shortcuts); - } - - private ShortcutInfo makeShortcut(int i) { - try { - IntentFilter filter = new IntentFilter(Intent.ACTION_SEND, "image/jpeg"); - - ComponentName component = new ComponentName("foo.bar", "foo.bar" + ".MainActivity"); - ShortcutInfo.Builder b = new ShortcutInfo.Builder(instrumentation.getContext(), "" + i) - .setActivity(component) - .setShortLabel("short chooser label " + i) - .setLongLabel("long chooser label" + i) - .setRank(i) - .setIntent(createSendImageIntent()) - .setIcon(Icon.createWithResource(instrumentation.getContext(), - android.R.drawable.ic_menu_add)) - .addChooserIntentFilter( - filter, - component); - - sOverrides.createPackageManager = pm -> { - final PackageManager spied = spy(pm); - try { - doAnswer(invocation -> ResolverDataProvider.createActivityInfo(i)) - .when(spied).getActivityInfo( - Mockito.isA(ComponentName.class), Mockito.anyInt()); - } catch (Exception e) { - // this is ok, just not found - e.printStackTrace(); - } - return spied; - }; - return b.build(); - } catch (Exception e) {return null;} - } }
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 0dac2602740f..c446f3c79ea8 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -18,7 +18,6 @@ package com.android.internal.app; import android.app.usage.UsageStatsManager; import android.content.Context; -import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import java.util.function.Function; @@ -75,11 +74,6 @@ public class ChooserWrapperActivity extends ChooserActivity { return super.getPackageManager(); } - @Override - public LauncherApps getLauncherApps() { - return sOverrides.launcherApps; - } - /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -88,7 +82,6 @@ public class ChooserWrapperActivity extends ChooserActivity { static class OverrideData { @SuppressWarnings("Since15") public Function<PackageManager, PackageManager> createPackageManager; - public LauncherApps launcherApps; public Function<TargetInfo, Boolean> onSafelyStartCallback; public ResolverListController resolverListController; public Boolean isVoiceInteraction; @@ -97,7 +90,6 @@ public class ChooserWrapperActivity extends ChooserActivity { onSafelyStartCallback = null; isVoiceInteraction = null; createPackageManager = null; - launcherApps = mock(LauncherApps.class); resolverListController = mock(ResolverListController.class); } } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 344f3c83ba6c..86ab3dc227f4 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -181,6 +181,9 @@ <allow-in-power-save package="com.android.cellbroadcastreceiver" /> <allow-in-power-save package="com.android.shell" /> + <!-- STOPSHIP(b/36856786): Revert this once it is fixed properly --> + <allow-in-power-save package="com.google.android.apps.enterprise.dmagent" /> + <!-- These are the packages that are white-listed to be able to run as system user --> <system-user-whitelisted-app package="com.android.settings" /> diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index d69f67d138a1..218b857ce83c 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -289,7 +289,7 @@ import java.util.function.DoubleUnaryOperator; * and <code>(1.0, 0.0, 0.0, 0.5)</code>.</p> */ @AnyThread -public final class Color { +public class Color { @ColorInt public static final int BLACK = 0xFF000000; @ColorInt public static final int DKGRAY = 0xFF444444; @ColorInt public static final int GRAY = 0xFF888888; diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 7f579a2fd404..deafb6638ece 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -20,6 +20,7 @@ import android.annotation.CheckResult; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import java.io.PrintWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -170,6 +171,10 @@ public final class Rect implements Parcelable { * or null if the string is not of that form. */ public static Rect unflattenFromString(String str) { + if (TextUtils.isEmpty(str)) { + return null; + } + Matcher matcher = UnflattenHelper.getMatcher(str); if (!matcher.matches()) { return null; @@ -179,7 +184,7 @@ public final class Rect implements Parcelable { Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4))); } - + /** * Print short representation to given writer. * @hide diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c7796cdd4cc4..4adbf79d562c 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2540,6 +2540,44 @@ public class AudioManager { /** * @hide + * Notifies an application with a focus listener of gain or loss of audio focus. + * This method can only be used by owners of an {@link AudioPolicy} configured with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true. + * @param afi the recipient of the focus change, that has previously requested audio focus, and + * that was received by the {@code AudioPolicy} through + * {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}. + * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN}, + * {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or + * {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}) + * or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS}, + * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}, + * or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}). + * <br>For the focus gain, the change type should be the same as the app requested. + * @param ap a valid registered {@link AudioPolicy} configured as a focus policy. + * @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or + * {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or + * if there was an error sending the request. + * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null. + */ + @SystemApi + public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange, + @NonNull AudioPolicy ap) { + if (afi == null) { + throw new NullPointerException("Illegal null AudioFocusInfo"); + } + if (ap == null) { + throw new NullPointerException("Illegal null AudioPolicy"); + } + final IAudioService service = getService(); + try { + return service.dispatchFocusChange(afi, focusChange, ap.cb()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Used internally by telephony package to abandon audio focus, typically after a call or * when ringing ends and the call is rejected or not answered. * Should match one or more calls to {@link #requestAudioFocusForCall(int, int)}. @@ -2548,7 +2586,7 @@ public class AudioManager { final IAudioService service = getService(); try { service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID, - null /*AudioAttributes, legacy behavior*/); + null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2579,7 +2617,7 @@ public class AudioManager { final IAudioService service = getService(); try { status = service.abandonAudioFocus(mAudioFocusDispatcher, - getIdForAudioFocusListener(l), aa); + getIdForAudioFocusListener(l), aa, getContext().getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2787,7 +2825,7 @@ public class AudioManager { final IAudioService service = getService(); try { String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), - policy.hasFocusListener()); + policy.hasFocusListener(), policy.isFocusPolicy()); if (regId == null) { return ERROR; } else { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 58559843f1f9..884d41e59ada 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; +import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; @@ -122,7 +123,8 @@ interface IAudioService { IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb); - int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa); + int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa, + in String callingPackageName); void unregisterAudioFocusClient(String clientId); @@ -164,7 +166,7 @@ interface IAudioService { boolean isHdmiSystemAudioSupported(); String registerAudioPolicy(in AudioPolicyConfig policyConfig, - in IAudioPolicyCallback pcb, boolean hasFocusListener); + in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy); oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); @@ -196,5 +198,8 @@ interface IAudioService { int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr); + int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange, + in IAudioPolicyCallback pcb); + // WARNING: read warning at top of file, it is recommended to add new methods at the end } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index cdc1d60fa789..4675e327ce25 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -746,14 +746,19 @@ public class MediaRecorder } /** - * Sets the video encoding profile for recording. Call this method before prepare(). - * Prepare() may perform additional checks on the parameter to make sure whether the - * specified profile and level are applicable, and sometimes the passed profile or - * level will be discarded due to codec capablity or to ensure the video recording - * can proceed smoothly based on the capabilities of the platform. - * @hide + * Sets the desired video encoding profile and level for recording. The profile and level + * must be valid for the video encoder set by {@link #setVideoEncoder}. This method can + * called before or after {@link #setVideoEncoder} but it must be called before {@link #prepare}. + * {@code prepare()} may perform additional checks on the parameter to make sure that the specified + * profile and level are applicable, and sometimes the passed profile or level will be + * discarded due to codec capablity or to ensure the video recording can proceed smoothly + * based on the capabilities of the platform. <br>Application can also use the + * {@link MediaCodecInfo.CodecCapabilities#profileLevels} to query applicable combination of profile + * and level for the corresponding format. Note that the requested profile/level may not be supported by + * the codec that is actually being used by this MediaRecorder instance. * @param profile declared in {@link MediaCodecInfo.CodecProfileLevel}. * @param level declared in {@link MediaCodecInfo.CodecProfileLevel}. + * @throws IllegalArgumentException when an invalid profile or level value is used. */ public void setVideoEncodingProfileLevel(int profile, int level) { if (profile <= 0) { @@ -1281,3 +1286,4 @@ public class MediaRecorder @Override protected void finalize() { native_finalize(); } } + diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 423b4678441c..61d642f50179 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -68,6 +68,7 @@ public class AudioPolicy { private int mStatus; private String mRegistrationId; private AudioPolicyStatusListener mStatusListener; + private boolean mIsFocusPolicy; /** * The behavior of a policy with regards to audio focus where it relies on the application @@ -96,12 +97,14 @@ public class AudioPolicy { public AudioPolicyConfig getConfig() { return mConfig; } /** @hide */ public boolean hasFocusListener() { return mFocusListener != null; } + /** @hide */ + public boolean isFocusPolicy() { return mIsFocusPolicy; } /** * The parameter is guaranteed non-null through the Builder */ private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, - AudioPolicyFocusListener fl, AudioPolicyStatusListener sl) { + AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) { mConfig = config; mStatus = POLICY_STATUS_UNREGISTERED; mContext = context; @@ -116,10 +119,12 @@ public class AudioPolicy { } mFocusListener = fl; mStatusListener = sl; + mIsFocusPolicy = isFocusPolicy; } /** - * Builder class for {@link AudioPolicy} objects + * Builder class for {@link AudioPolicy} objects. + * By default the policy to be created doesn't govern audio focus decisions. */ @SystemApi public static class Builder { @@ -128,6 +133,7 @@ public class AudioPolicy { private Looper mLooper; private AudioPolicyFocusListener mFocusListener; private AudioPolicyStatusListener mStatusListener; + private boolean mIsFocusPolicy = false; /** * Constructs a new Builder with no audio mixes. @@ -179,6 +185,21 @@ public class AudioPolicy { } /** + * Declares whether this policy will grant and deny audio focus through + * the {@link AudioPolicy.AudioPolicyStatusListener}. + * If set to {@code true}, it is mandatory to set an + * {@link AudioPolicy.AudioPolicyStatusListener} in order to successfully build + * an {@code AudioPolicy} instance. + * @param enforce true if the policy will govern audio focus decisions. + * @return the same Builder instance. + */ + @SystemApi + public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) { + mIsFocusPolicy = isFocusPolicy; + return this; + } + + /** * Sets the audio policy status listener. * @param l a {@link AudioPolicy.AudioPolicyStatusListener} */ @@ -187,6 +208,14 @@ public class AudioPolicy { mStatusListener = l; } + /** + * Combines all of the attributes that have been set on this {@code Builder} and returns a + * new {@link AudioPolicy} object. + * @return a new {@code AudioPolicy} object. + * @throws IllegalStateException if there is no + * {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured + * as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}. + */ @SystemApi public AudioPolicy build() { if (mStatusListener != null) { @@ -195,8 +224,12 @@ public class AudioPolicy { mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY; } } + if (mIsFocusPolicy && mFocusListener == null) { + throw new IllegalStateException("Cannot be a focus policy without " + + "an AudioPolicyFocusListener"); + } return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, - mFocusListener, mStatusListener); + mFocusListener, mStatusListener, mIsFocusPolicy); } } @@ -402,6 +435,24 @@ public class AudioPolicy { public static abstract class AudioPolicyFocusListener { public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {} public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {} + /** + * Called whenever an application requests audio focus. + * Only ever called if the {@link AudioPolicy} was built with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. + * @param afi information about the focus request and the requester + * @param requestResult the result that was returned synchronously by the framework to the + * application, {@link #AUDIOFOCUS_REQUEST_FAILED},or + * {@link #AUDIOFOCUS_REQUEST_DELAYED}. + */ + public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {} + /** + * Called whenever an application abandons audio focus. + * Only ever called if the {@link AudioPolicy} was built with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. + * @param afi information about the focus request being abandoned and the original + * requester. + */ + public void onAudioFocusAbandon(AudioFocusInfo afi) {} } private void onPolicyStatusChange() { @@ -439,6 +490,22 @@ public class AudioPolicy { } } + public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) { + sendMsg(MSG_FOCUS_REQUEST, afi, requestResult); + if (DEBUG) { + Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client=" + + afi.getClientId() + "reqRes=" + requestResult); + } + } + + public void notifyAudioFocusAbandon(AudioFocusInfo afi) { + sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */); + if (DEBUG) { + Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client=" + + afi.getClientId()); + } + } + public void notifyMixStateUpdate(String regId, int state) { for (AudioMix mix : mConfig.getMixes()) { if (mix.getRegistration().equals(regId)) { @@ -459,6 +526,8 @@ public class AudioPolicy { private final static int MSG_FOCUS_GRANT = 1; private final static int MSG_FOCUS_LOSS = 2; private final static int MSG_MIX_STATE_UPDATE = 3; + private final static int MSG_FOCUS_REQUEST = 4; + private final static int MSG_FOCUS_ABANDON = 5; private class EventHandler extends Handler { public EventHandler(AudioPolicy ap, Looper looper) { @@ -488,6 +557,20 @@ public class AudioPolicy { mStatusListener.onMixStateUpdate((AudioMix) msg.obj); } break; + case MSG_FOCUS_REQUEST: + if (mFocusListener != null) { + mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1); + } else { // should never be null, but don't crash + Log.e(TAG, "Invalid null focus listener for focus request event"); + } + break; + case MSG_FOCUS_ABANDON: + if (mFocusListener != null) { // should never be null + mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj); + } else { // should never be null, but don't crash + Log.e(TAG, "Invalid null focus listener for focus abandon event"); + } + break; default: Log.e(TAG, "Unknown event " + msg.what); } diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl index ad8af15b15f2..86abbb4dc8d9 100644 --- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl +++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl @@ -22,9 +22,12 @@ import android.media.AudioFocusInfo; */ oneway interface IAudioPolicyCallback { - // callbacks for audio focus + // callbacks for audio focus listening void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult); void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified); + // callback for audio focus policy + void notifyAudioFocusRequest(in AudioFocusInfo afi, int requestResult); + void notifyAudioFocusAbandon(in AudioFocusInfo afi); // callback for mix activity status update void notifyMixStateUpdate(in String regId, int state); diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 06fc4bce3c76..d8c3eca300a3 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -16,6 +16,7 @@ package android.media.tv; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; @@ -840,7 +841,7 @@ public final class TvContract { public interface BasePreviewProgramColumns extends BaseProgramColumns { /** @hide */ - @StringDef({ + @IntDef({ TYPE_MOVIE, TYPE_TV_SERIES, TYPE_TV_SEASON, @@ -862,87 +863,87 @@ public final class TvContract { * * @see #COLUMN_TYPE */ - String TYPE_MOVIE = "TYPE_MOVIE"; + int TYPE_MOVIE = 0; /** * The program type for TV series. * * @see #COLUMN_TYPE */ - String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + int TYPE_TV_SERIES = 1; /** * The program type for TV season. * * @see #COLUMN_TYPE */ - String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + int TYPE_TV_SEASON = 2; /** * The program type for TV episode. * * @see #COLUMN_TYPE */ - String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + int TYPE_TV_EPISODE = 3; /** * The program type for clip. * * @see #COLUMN_TYPE */ - String TYPE_CLIP = "TYPE_CLIP"; + int TYPE_CLIP = 4; /** * The program type for event. * * @see #COLUMN_TYPE */ - String TYPE_EVENT = "TYPE_EVENT"; + int TYPE_EVENT = 5; /** * The program type for channel. * * @see #COLUMN_TYPE */ - String TYPE_CHANNEL = "TYPE_CHANNEL"; + int TYPE_CHANNEL = 6; /** * The program type for track. * * @see #COLUMN_TYPE */ - String TYPE_TRACK = "TYPE_TRACK"; + int TYPE_TRACK = 7; /** * The program type for album. * * @see #COLUMN_TYPE */ - String TYPE_ALBUM = "TYPE_ALBUM"; + int TYPE_ALBUM = 8; /** * The program type for artist. * * @see #COLUMN_TYPE */ - String TYPE_ARTIST = "TYPE_ARTIST"; + int TYPE_ARTIST = 9; /** * The program type for playlist. * * @see #COLUMN_TYPE */ - String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + int TYPE_PLAYLIST = 10; /** * The program type for station. * * @see #COLUMN_TYPE */ - String TYPE_STATION = "TYPE_STATION"; + int TYPE_STATION = 11; /** @hide */ - @StringDef({ + @IntDef({ ASPECT_RATIO_16_9, ASPECT_RATIO_3_2, ASPECT_RATIO_1_1, @@ -957,7 +958,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + int ASPECT_RATIO_16_9 = 0; /** * The aspect ratio for 3:2. @@ -965,7 +966,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + int ASPECT_RATIO_3_2 = 1; /** * The aspect ratio for 1:1. @@ -973,7 +974,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + int ASPECT_RATIO_1_1 = 2; /** * The aspect ratio for 2:3. @@ -981,10 +982,10 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + int ASPECT_RATIO_2_3 = 3; /** @hide */ - @StringDef({ + @IntDef({ AVAILABILITY_AVAILABLE, AVAILABILITY_FREE_WITH_SUBSCRIPTION, AVAILABILITY_PAID_CONTENT, @@ -997,15 +998,14 @@ public final class TvContract { * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + int AVAILABILITY_AVAILABLE = 0; /** * The availability for "free with subscription". * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_FREE_WITH_SUBSCRIPTION = - "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; /** * The availability for "paid content, either to-own or rental @@ -1013,72 +1013,72 @@ public final class TvContract { * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + int AVAILABILITY_PAID_CONTENT = 2; /** @hide */ - @StringDef({ + @IntDef({ + INTERACTION_TYPE_VIEWS, INTERACTION_TYPE_LISTENS, INTERACTION_TYPE_FOLLOWERS, INTERACTION_TYPE_FANS, INTERACTION_TYPE_LIKES, INTERACTION_TYPE_THUMBS, - INTERACTION_TYPE_VIEWS, INTERACTION_TYPE_VIEWERS, }) @Retention(RetentionPolicy.SOURCE) public @interface InteractionType {} /** - * The interaction type for "listens". + * The interaction type for "views". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + int INTERACTION_TYPE_VIEWS = 0; /** - * The interaction type for "followers". + * The interaction type for "listens". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + int INTERACTION_TYPE_LISTENS = 1; /** - * The interaction type for "fans". + * The interaction type for "followers". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + int INTERACTION_TYPE_FOLLOWERS = 2; /** - * The interaction type for "likes". + * The interaction type for "fans". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + int INTERACTION_TYPE_FANS = 3; /** - * The interaction type for "thumbs". + * The interaction type for "likes". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + int INTERACTION_TYPE_LIKES = 4; /** - * The interaction type for "views". + * The interaction type for "thumbs". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + int INTERACTION_TYPE_THUMBS = 5; /** * The interaction type for "viewers". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + int INTERACTION_TYPE_VIEWERS = 6; /** @hide */ - @StringDef({ + @IntDef({ REVIEW_RATING_STYLE_STARS, REVIEW_RATING_STYLE_THUMBS_UP_DOWN, REVIEW_RATING_STYLE_PERCENTAGE, @@ -1091,23 +1091,21 @@ public final class TvContract { * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + int REVIEW_RATING_STYLE_STARS = 0; /** * The review rating style for thumbs-up and thumbs-down rating. * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = - "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; /** * The review rating style for 0 to 100 point system. * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_PERCENTAGE = - "REVIEW_RATING_STYLE_PERCENTAGE"; + int REVIEW_RATING_STYLE_PERCENTAGE = 2; /** * The type of this program content. @@ -1129,7 +1127,7 @@ public final class TvContract { * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW} * channel. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_TYPE = "type"; @@ -1142,7 +1140,7 @@ public final class TvContract { * {@link #ASPECT_RATIO_1_1}, and * {@link #ASPECT_RATIO_2_3}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; @@ -1155,7 +1153,7 @@ public final class TvContract { * {@link #ASPECT_RATIO_1_1}, and * {@link #ASPECT_RATIO_2_3}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; @@ -1188,7 +1186,7 @@ public final class TvContract { * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and * {@link #AVAILABILITY_PAID_CONTENT}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_AVAILABILITY = "availability"; @@ -1299,18 +1297,17 @@ public final class TvContract { String COLUMN_DURATION_MILLIS = "duration_millis"; /** - * The intent URI which is launched when the preview video is selected. + * The intent URI which is launched when the preview program is selected. * * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME} * and converted back to the original intent with {@link Intent#parseUri}. The intent is - * launched when the user selects the preview video item. + * launched when the user selects the preview program item. * * <p>Can be empty. * * <p>Type: TEXT */ - String COLUMN_APP_LINK_INTENT_URI = - "app_link_intent_uri"; + String COLUMN_INTENT_URI = "intent_uri"; /** * The flag indicating whether this program is transient or not. @@ -1328,15 +1325,15 @@ public final class TvContract { * The type of interaction for this TV program. * * <p> The value should match one of the followings: + * {@link #INTERACTION_TYPE_VIEWS}, * {@link #INTERACTION_TYPE_LISTENS}, * {@link #INTERACTION_TYPE_FOLLOWERS}, * {@link #INTERACTION_TYPE_FANS}, * {@link #INTERACTION_TYPE_LIKES}, - * {@link #INTERACTION_TYPE_THUMBS}, - * {@link #INTERACTION_TYPE_VIEWS}, and + * {@link #INTERACTION_TYPE_THUMBS}, and * {@link #INTERACTION_TYPE_VIEWERS}. * - * <p>Type: TEXT + * <p>Type: INTEGER * @see #COLUMN_INTERACTION_COUNT */ String COLUMN_INTERACTION_TYPE = "interaction_type"; @@ -1364,7 +1361,7 @@ public final class TvContract { * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS}, * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}. * - * <p>Type: TEXT + * <p>Type: INTEGER * @see #COLUMN_REVIEW_RATING */ String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; @@ -2725,7 +2722,7 @@ public final class TvContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; /** @hide */ - @StringDef({ + @IntDef({ WATCH_NEXT_TYPE_CONTINUE, WATCH_NEXT_TYPE_NEXT, WATCH_NEXT_TYPE_NEW, @@ -2740,7 +2737,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; + public static final int WATCH_NEXT_TYPE_CONTINUE = 0; /** * The watch next type for NEXT. Use this type when the user has watched one or more @@ -2750,7 +2747,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; + public static final int WATCH_NEXT_TYPE_NEXT = 1; /** * The watch next type for NEW. Use this type when the user had watched all of the available @@ -2760,7 +2757,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; + public static final int WATCH_NEXT_TYPE_NEW = 2; /** * The watch next type for WATCHLIST. Use this type when the user has elected to explicitly @@ -2769,7 +2766,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; /** * The "watch next" type of this program content. @@ -2782,7 +2779,7 @@ public final class TvContract { * * <p>This is a required field. * - * <p>Type: TEXT + * <p>Type: INTEGER */ public static final String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index c7bed9bc1685..c82a1f6a646a 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -153,6 +153,7 @@ LIBANDROID { ANativeWindow_acquire; ANativeWindow_fromSurface; ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13 + ANativeWindow_toSurface; # introduced=26 ANativeWindow_getFormat; ANativeWindow_getHeight; ANativeWindow_getWidth; diff --git a/native/android/native_window_jni.cpp b/native/android/native_window_jni.cpp index dc3040533974..859c550db94d 100644 --- a/native/android/native_window_jni.cpp +++ b/native/android/native_window_jni.cpp @@ -20,6 +20,7 @@ #include <android/native_window.h> #include <system/window.h> +#include <gui/Surface.h> #include <utils/StrongPointer.h> #include <android_runtime/android_view_Surface.h> @@ -33,3 +34,11 @@ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) { } return win.get(); } + +jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) { + if (window == NULL) { + return NULL; + } + sp<Surface> surface = static_cast<Surface*>(window); + return android_view_Surface_createFromSurface(env, surface); +} diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml index 1f35d3e286e9..ea538fb320b1 100644 --- a/packages/SettingsLib/res/values/attrs.xml +++ b/packages/SettingsLib/res/values/attrs.xml @@ -36,6 +36,9 @@ <declare-styleable name="WifiEncryptionState"> <attr name="state_encrypted" format="boolean" /> </declare-styleable> + <declare-styleable name="WifiMeteredState"> + <attr name="state_metered" format="boolean" /> + </declare-styleable> <declare-styleable name="WifiSavedState"> <attr name="state_saved" format="boolean" /> </declare-styleable> diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index c2ce7c9fd5de..8833fb8cab53 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -1394,7 +1394,9 @@ public class ApplicationsState { @Override public boolean filterApp(AppEntry entry) { - if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + if (AppUtils.isInstant(entry.info)) { + return false; + } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { return true; } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return true; @@ -1407,6 +1409,23 @@ public class ApplicationsState { } }; + /** + * Displays a combined list with "downloaded" and "visible in launcher" apps only. + */ + public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() { + + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + return AppUtils.isInstant(entry.info) + || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry); + } + + }; + public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() { @Override public void init() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java index 69b45e5541b2..9ea7a4af8d12 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java @@ -71,14 +71,15 @@ public class BluetoothDiscoverableTimeoutReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (intent.getAction() == null || !intent.getAction().equals(INTENT_DISCOVERABLE_TIMEOUT)) { + return; + } LocalBluetoothAdapter localBluetoothAdapter = LocalBluetoothAdapter.getInstance(); - - if(localBluetoothAdapter != null && + if(localBluetoothAdapter != null && localBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { Log.d(TAG, "Disable discoverable..."); - localBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); - } else { + } else { Log.e(TAG, "localBluetoothAdapter is NULL!!"); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index a3ae9269b1f7..abd4e294fecb 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -103,7 +103,7 @@ public class CachedBluetoothDeviceManager { */ public String getName(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); - if (cachedDevice != null) { + if (cachedDevice != null && cachedDevice.getName() != null) { return cachedDevice.getName(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java index 0f443d6791e3..61ca13d2819a 100755 --- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java +++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java @@ -63,7 +63,7 @@ public class BatteryMeterDrawableBase extends Drawable { mPlusPaint; private float mTextHeight, mWarningTextHeight; private int mIconTint = Color.WHITE; - private float mOldDarkIntensity = 0f; + private float mOldDarkIntensity = -1f; private int mHeight; private int mWidth; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index a9aaa05cb674..8f8167ee8187 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -44,6 +44,10 @@ public class AccessPointPreference extends Preference { R.attr.state_encrypted }; + private static final int[] STATE_METERED = { + R.attr.state_metered + }; + private static final int[] wifi_friction_attributes = { R.attr.wifi_friction }; private final StateListDrawable mFrictionSld; @@ -179,6 +183,8 @@ public class AccessPointPreference extends Preference { } if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { mFrictionSld.setState(STATE_SECURED); + } else if (mAccessPoint.getConfig() != null && mAccessPoint.getConfig().meteredHint) { + mFrictionSld.setState(STATE_METERED); } Drawable drawable = mFrictionSld.getCurrent(); frictionImageView.setImageDrawable(drawable); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 6f52dcace323..ec94841c7d0d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -26,7 +26,9 @@ public class WifiStatusTracker { private final WifiManager mWifiManager; public boolean enabled; + public int state; public boolean connected; + public boolean connecting; public String ssid; public int rssi; public int level; @@ -39,11 +41,18 @@ public class WifiStatusTracker { public void handleBroadcast(Intent intent) { String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN); + enabled = state == WifiManager.WIFI_STATE_ENABLED; + + enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { final NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + connecting = networkInfo != null && !networkInfo.isConnected() + && networkInfo.isConnectedOrConnecting(); connected = networkInfo != null && networkInfo.isConnected(); WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java index e204a3a65c72..6a029f0be64d 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java @@ -110,6 +110,70 @@ public class ApplicationsStateTest { } @Test + public void testDownloadAndLauncherAndInstantAcceptsCorrectApps() { + // should include instant apps + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + when(mEntry.info.isInstantApp()).thenReturn(true); + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should included updated system apps + when(mEntry.info.isInstantApp()).thenReturn(false); + mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should not include system apps other than the home app + mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM; + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isFalse(); + + // should include the home app + mEntry.isHomeApp = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should include any System app with a launcher entry + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + } + + @Test + public void testDownloadAndLauncherAcceptsCorrectApps() { + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + + // should included updated system apps + when(mEntry.info.isInstantApp()).thenReturn(false); + mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + + // should not include system apps other than the home app + mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM; + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isFalse(); + + // should include the home app + mEntry.isHomeApp = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + + // should include any System app with a launcher entry + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + } + + @Test public void testInstantFilterAcceptsInstantApp() { when(mEntry.info.isInstantApp()).thenReturn(true); assertThat(ApplicationsState.FILTER_INSTANT.filterApp(mEntry)).isTrue(); diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 635c96f23778..53c5b1b6b2bc 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -34,6 +34,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-recyclerview \ android-support-v7-preference \ android-support-v7-appcompat \ + android-support-v7-mediarouter \ android-support-v14-preference \ android-support-v17-leanback diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_group_1_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_group_1_animation.xml new file mode 100644 index 000000000000..26c6aa706cbf --- /dev/null +++ b/packages/SystemUI/res/anim/ic_bluetooth_transient_group_1_animation.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="83" + android:propertyName="scaleX" + android:valueFrom="1.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="333" + android:propertyName="scaleX" + android:valueFrom="0.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="83" + android:propertyName="scaleX" + android:valueFrom="0.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="333" + android:propertyName="scaleX" + android:valueFrom="1.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + </set> + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="83" + android:propertyName="scaleY" + android:valueFrom="1.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="333" + android:propertyName="scaleY" + android:valueFrom="0.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="83" + android:propertyName="scaleY" + android:valueFrom="0.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + </set> +</set> diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_group_2_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_group_2_animation.xml new file mode 100644 index 000000000000..e1f8989b9a21 --- /dev/null +++ b/packages/SystemUI/res/anim/ic_bluetooth_transient_group_2_animation.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="83" + android:propertyName="scaleX" + android:valueFrom="0.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="333" + android:propertyName="scaleX" + android:valueFrom="1.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@interpolator/ic_bluetooth_transient_animation_interpolator_0" /> + <objectAnimator + android:duration="83" + android:propertyName="scaleX" + android:valueFrom="1.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + </set> + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="83" + android:propertyName="scaleY" + android:valueFrom="0.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="333" + android:propertyName="scaleY" + android:valueFrom="1.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@interpolator/ic_bluetooth_transient_animation_interpolator_0" /> + <objectAnimator + android:duration="83" + android:propertyName="scaleY" + android:valueFrom="1.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + </set> +</set> diff --git a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml new file mode 100644 index 000000000000..919a4ddd4f08 --- /dev/null +++ b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="333" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="16" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="316" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="16" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="316" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="16" + android:propertyName="pathData" + android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="333" + android:propertyName="fillAlpha" + android:valueFrom="0.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="16" + android:propertyName="fillAlpha" + android:valueFrom="0.0" + android:valueTo="1.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml new file mode 100644 index 000000000000..76026af392ec --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="ic_bluetooth_transient" + android:width="48dp" + android:viewportWidth="48" + android:height="48dp" + android:viewportHeight="48" > + <group + android:name="ic_signal_wifi_4_bar_48px_outlines_" + android:translateX="21.9995" + android:translateY="25.73401" > + <group + android:name="ic_signal_wifi_4_bar_48px_outlines__pivot" + android:translateX="-23.21545" + android:translateY="-18.86649" > + <group + android:name="bluetooth" + android:translateX="22.08789" + android:translateY="18.72031" > + <group + android:name="bluetooth_pivot" + android:translateX="-22.08789" + android:translateY="-18.72031" > + <group + android:name="cross" + android:rotation="-1.88453" > + <path + android:name="extented_cross" + android:pathData="M 10.6188659668,6.56344604492 c 0.0,0.0 21.7386016846,23.1297454834 21.7386016846,23.1297454834" + android:strokeColor="#FFFFFFFF" + android:strokeWidth="4" /> + </group> + <group + android:name="bluetooth_0" + android:translateX="23.38789" + android:translateY="18.72031" > + <path + android:name="b_shape_merged" + android:pathData="M 11.3999938965,-8.60000610352 c 0.0,0.0 -11.3999938965,-11.3999938965 -11.3999938965,-11.3999938965 c 0.0,0.0 -2.0,0.0 -2.0,0.0 c 0.0,0.0 0.0,15.1999969482 0.0,15.1999969482 c 0.0,0.0 -9.19999694824,-9.19999694824 -9.19999694824,-9.19999694824 c 0.0,0.0 -2.80000305176,2.80000305176 -2.80000305176,2.80000305176 c 0.0,0.0 11.1999969482,11.1999969482 11.1999969482,11.1999969482 c 0.0,0.0 -11.1999969482,11.1999969482 -11.1999969482,11.1999969482 c 0.0,0.0 2.80000305176,2.80000305176 2.80000305176,2.80000305176 c 0.0,0.0 9.19999694824,-9.19999694824 9.19999694824,-9.19999694824 c 0.0,0.0 0.0,15.1999969482 0.0,15.1999969482 c 0.0,0.0 2.0,0.0 2.0,0.0 c 0.0,0.0 11.3999938965,-11.3999938965 11.3999938965,-11.3999938965 c 0.0,0.0 -8.59999084473,-8.60000610352 -8.59999084473,-8.60000610352 c 0.0,0.0 8.59999084473,-8.60000610352 8.59999084473,-8.60000610352 Z M 2.0,-12.3000030518 c 0.0,0.0 3.80000305176,3.80000305176 3.80000305176,3.80000305176 c 0.0,0.0 -3.80000305176,3.69999694824 -3.80000305176,3.69999694824 c 0.0,0.0 0.0,-7.5 0.0,-7.5 Z M 5.80000305176,8.60000610352 c 0.0,0.0 -3.80000305176,3.69999694824 -3.80000305176,3.69999694824 c 0.0,0.0 0.0,-7.5 0.0,-7.5 c 0.0,0.0 3.80000305176,3.80000305176 3.80000305176,3.80000305176 Z" + android:fillColor="#FFFFFFFF" /> + </group> + </group> + </group> + <group + android:name="dot_left" + android:translateX="20.16992" + android:translateY="18.64258" > + <group + android:name="dot_left_pivot" + android:translateX="-20.16992" + android:translateY="-18.64258" > + <group + android:name="group_1" + android:translateX="9.38789" + android:translateY="18.72031" > + <group + android:name="group_1_pivot" + android:translateX="-9.38789" + android:translateY="-18.72031" > + <path + android:name="dot_left_0" + android:pathData="M 13.3878936768,18.7203063965 c 0.0,0.0 -4.0,-4.0 -4.0,-4.0 c 0.0,0.0 -4.0,4.0 -4.0,4.0 c 0.0,0.0 4.0,4.0 4.0,4.0 c 0.0,0.0 4.0,-4.0 4.0,-4.0 Z" + android:fillColor="#FFFFFFFF" /> + </group> + </group> + </group> + </group> + <group + android:name="dot_right" + android:translateX="26.16094" + android:translateY="18.60898" > + <group + android:name="dot_right_pivot" + android:translateX="-26.16094" + android:translateY="-18.60898" > + <group + android:name="group_2" + android:translateX="37.38789" + android:translateY="18.72031" + android:scaleX="0" + android:scaleY="0" > + <group + android:name="group_1_pivot_0" + android:translateX="-37.38789" + android:translateY="-18.72031" > + <path + android:name="dot_right_0" + android:pathData="M 37.3878936768,14.7203063965 c 0.0,0.0 -4.0,4.0 -4.0,4.0 c 0.0,0.0 4.0,4.0 4.0,4.0 c 0.0,0.0 4.0,-4.0 4.0,-4.0 c 0.0,0.0 -4.0,-4.0 -4.0,-4.0 Z" + android:fillColor="#FFFFFFFF" /> + </group> + </group> + </group> + </group> + </group> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..f7eb23c1c89d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_bluetooth_transient" > + <target + android:name="group_1" + android:animation="@anim/ic_bluetooth_transient_group_1_animation" /> + <target + android:name="group_2" + android:animation="@anim/ic_bluetooth_transient_group_2_animation" /> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml new file mode 100644 index 000000000000..880e1bb075c7 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="ic_signal_wifi_transient" + android:width="48dp" + android:viewportWidth="48" + android:height="48dp" + android:viewportHeight="48" > + <group + android:name="ic_signal_wifi_4_bar_48px_2" + android:translateX="24.25" + android:translateY="25.73401" > + <group + android:name="ic_signal_wifi_4_bar_48px_2_pivot" + android:translateX="-23.21545" + android:translateY="-18.86649" > + <group + android:name="wifi_2" + android:translateX="23.481" + android:translateY="18.71151" > + <path + android:name="wifi" + android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:fillColor="#FFFFFFFF" + android:fillAlpha="0.5" /> + </group> + <group + android:name="wifi_0" + android:translateX="23.481" + android:translateY="18.71151" > + <path + android:name="wifi_1" + android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" + android:fillColor="#FFFFFFFF" + android:fillAlpha="0" /> + </group> + </group> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..9f5aaebaf650 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_signal_wifi_transient" > + <target + android:name="wifi_1" + android:animation="@anim/ic_signal_wifi_transient_wifi_1_animation" /> +</animated-vector> diff --git a/packages/SystemUI/res/interpolator/ic_bluetooth_transient_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_bluetooth_transient_animation_interpolator_0.xml new file mode 100644 index 000000000000..c421f9e9e81c --- /dev/null +++ b/packages/SystemUI/res/interpolator/ic_bluetooth_transient_animation_interpolator_0.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c 0.16666666667,0.0 0.83333333333,1.0 1.0,1.0" /> diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml index 6bd79a4b4ae4..8078c4121424 100644 --- a/packages/SystemUI/res/layout/menu_ime.xml +++ b/packages/SystemUI/res/layout/menu_ime.xml @@ -27,6 +27,7 @@ android:layout_marginEnd="2dp" android:scaleType="centerInside" systemui:keyCode="82" + systemui:playSound="false" android:visibility="invisible" android:contentDescription="@string/accessibility_menu" /> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index f405943811cf..008fcf0c62b4 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -20,6 +20,8 @@ <attr name="keyCode" format="integer" /> <!-- does this button generate longpress / repeat events? --> <attr name="keyRepeat" format="boolean" /> + <!-- Should this button play sound effects, default true --> + <attr name="playSound" format="boolean" /> <attr name="android:contentDescription" /> </declare-styleable> <declare-styleable name="ToggleSliderView"> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ad39f5475554..a8cf3daec41e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1962,4 +1962,31 @@ <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] --> <string name="mobile_data">Mobile data</string> + <!-- Label for when wifi is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="wifi_is_off">Wi-Fi is off</string> + + <!-- Label for when bluetooth is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="bt_is_off">Bluetooth is off</string> + + <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="dnd_is_off">Do Not Disturb is off</string> + + <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>). Keep current settings?</string> + + <!-- Prompt for when Do not disturb is on from app in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_app">Do Not Disturb was turned on by an app (<xliff:g name="app">%s</xliff:g>). Keep current settings?</string> + + <!-- Prompt for when Do not disturb is on from automatic rule or app in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_auto_rule_app">Do Not Disturb was turned on by an automatic rule or app. Keep current settings?</string> + + <!-- Description of Do Not Disturb option in QS that ends at the specified time[CHAR LIMIT=20] --> + <string name="qs_dnd_until">Until <xliff:g name="time">%s</xliff:g></string> + + <!-- Do Not Disturb button to keep the current settings [CHAR LIMIT=20] --> + <string name="qs_dnd_keep">Keep</string> + + <!-- Do Not Disturb button to change the current settings [CHAR LIMIT=20] --> + <string name="qs_dnd_replace">Replace</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 8c1062bf4cf4..d57e88cce351 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -98,6 +98,8 @@ public class BatteryMeterView extends LinearLayout implements addView(mBatteryIconView, mlp); updateShowPercent(); + // Init to not dark at all. + onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT); } public void setForceShowPercent(boolean show) { 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 17228b9c876c..bcf1957109de 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -417,17 +417,17 @@ public class PipMenuActivity extends Activity { } private void updateDismissFraction(float fraction) { - setDecorViewVisibility(true); int alpha; if (mMenuVisible) { - mMenuContainer.setAlpha(1-fraction); + mMenuContainer.setAlpha(1 - fraction); final float interpolatedAlpha = MENU_BACKGROUND_ALPHA * (1.0f - fraction) + DISMISS_BACKGROUND_ALPHA * fraction; - alpha = (int) (interpolatedAlpha*255); + alpha = (int) (interpolatedAlpha * 255); } else { - alpha = (int) (fraction*DISMISS_BACKGROUND_ALPHA*255); + alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255); } mBackgroundDrawable.setAlpha(alpha); + setDecorViewVisibility(alpha > 0); } private void notifyRegisterInputConsumer() { 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 dd8cd2b3655f..ad290c388cb0 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -113,6 +113,7 @@ public class PipManager implements BasePipManager { private IWindowManager mWindowManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; + private int mResumeResizePinnedStackRunnable = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); private List<MediaListener> mMediaListeners = new ArrayList<>(); @@ -133,7 +134,7 @@ public class PipManager implements BasePipManager { private final Runnable mResizePinnedStackRunnable = new Runnable() { @Override public void run() { - resizePinnedStack(mState); + resizePinnedStack(mResumeResizePinnedStackRunnable); } }; private final Runnable mClosePipRunnable = new Runnable() { @@ -364,16 +365,17 @@ public class PipManager implements BasePipManager { void resizePinnedStack(int state) { if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state); boolean wasStateNoPip = (mState == STATE_NO_PIP); - mState = state; + mResumeResizePinnedStackRunnable = state; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onPipResizeAboutToStart(); } if (mSuspendPipResizingReason != 0) { - if (DEBUG) Log.d(TAG, - "resizePinnedStack() deferring mSuspendPipResizingReason=" + - mSuspendPipResizingReason); + if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring" + + " mSuspendPipResizingReason=" + mSuspendPipResizingReason + + " mResumeResizePinnedStackRunnable=" + mResumeResizePinnedStackRunnable); return; } + mState = state; switch (mState) { case STATE_NO_PIP: mCurrentPipBounds = null; 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 4e4de15567d4..75e9d5ab063b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -114,7 +114,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final boolean enabled = mController.isBluetoothEnabled(); final boolean connected = mController.isBluetoothConnected(); - final boolean connecting = mController.isBluetoothConnecting(); + state.isTransient = mController.isBluetoothConnecting() + || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON; state.dualTarget = true; state.value = enabled; if (enabled) { @@ -124,8 +125,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { state.label = mController.getLastDeviceName(); state.contentDescription = mContext.getString( R.string.accessibility_bluetooth_name, state.label); - } else if (connecting) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connecting); + } else if (state.isTransient) { + state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth_connecting); state.label = mContext.getString(R.string.quick_settings_bluetooth_label); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 22b6a634cb9a..2725a329f6e0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -20,22 +20,29 @@ import android.content.Context; import android.content.Intent; import android.provider.Settings; import android.service.quicksettings.Tile; +import android.support.v7.app.MediaRouteChooserDialog; +import android.support.v7.app.MediaRouteControllerDialog; +import android.support.v7.media.MediaControlIntent; +import android.support.v7.media.MediaRouteSelector; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.Button; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.R.style; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; +import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSDetailItems; import com.android.systemui.qs.QSDetailItems.Item; import com.android.systemui.qs.QSHost; -import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; @@ -109,7 +116,6 @@ public class CastTile extends QSTileImpl<BooleanState> { if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); - mHost.openPanels(); }); return; } @@ -117,6 +123,29 @@ public class CastTile extends QSTileImpl<BooleanState> { } @Override + public void showDetail(boolean show) { + mUiHandler.post(() -> { + Context context = new ContextThemeWrapper(mContext, + R.style.Theme_AppCompat_Light_Dialog_Alert); + if (mState.value) { + MediaRouteControllerDialog dialog = new MediaRouteControllerDialog(context); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL); + dialog.show(); + } else { + // Instead of showing detail, show standard media routing UI. + MediaRouteChooserDialog dialog = new MediaRouteChooserDialog(context); + MediaRouteSelector selector = new MediaRouteSelector.Builder() + .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) + .build(); + dialog.setRouteSelector(selector); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL); + dialog.show(); + } + mHost.collapsePanels(); + }); + } + + @Override public CharSequence getTileLabel() { return mContext.getString(R.string.quick_settings_cast_title); } 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 04be7de644e3..9b3ee30a3ab0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -180,7 +180,7 @@ public class CellularTile extends QSTileImpl<SignalState> { private final CallbackInfo mInfo = new CallbackInfo(); @Override public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description) { + boolean activityIn, boolean activityOut, String description, boolean isTransient) { mInfo.wifiEnabled = enabled; refreshState(mInfo); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 79b4c4a87596..7d2d210727ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -157,7 +157,10 @@ public class WifiTile extends QSTileImpl<SignalState> { state.activityOut = cb.enabled && cb.activityOut; final StringBuffer minimalContentDescription = new StringBuffer(); final Resources r = mContext.getResources(); - if (!state.value) { + if (cb.isTransient) { + state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation); + state.label = r.getString(R.string.quick_settings_wifi_label); + } else if (!state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled); state.label = r.getString(R.string.quick_settings_wifi_label); } else if (wifiConnected) { @@ -182,7 +185,7 @@ public class WifiTile extends QSTileImpl<SignalState> { state.dualLabelContentDescription = r.getString( R.string.accessibility_quick_settings_open_settings, getTileLabel()); state.expandedAccessibilityClassName = Switch.class.getName(); - state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.state = state.value || cb.isTransient ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; } @Override @@ -226,6 +229,7 @@ public class WifiTile extends QSTileImpl<SignalState> { boolean activityIn; boolean activityOut; String wifiSignalContentDescription; + boolean isTransient; @Override public String toString() { @@ -237,6 +241,7 @@ public class WifiTile extends QSTileImpl<SignalState> { .append(",activityIn=").append(activityIn) .append(",activityOut=").append(activityOut) .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription) + .append(",isTransient=").append(isTransient) .append(']').toString(); } } @@ -246,7 +251,7 @@ public class WifiTile extends QSTileImpl<SignalState> { @Override public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description) { + boolean activityIn, boolean activityOut, String description, boolean isTransient) { if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled); mInfo.enabled = enabled; mInfo.connected = qsIcon.visible; @@ -255,6 +260,7 @@ public class WifiTile extends QSTileImpl<SignalState> { mInfo.activityIn = activityIn; mInfo.activityOut = activityOut; mInfo.wifiSignalContentDescription = qsIcon.contentDescription; + mInfo.isTransient = isTransient; 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 1c71da007fd0..3a43d304b1ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -355,9 +355,10 @@ public class SystemServicesProxy { rti.firstActiveTime = rti.lastActiveTime = i; if (i % 2 == 0) { rti.taskDescription = new ActivityManager.TaskDescription(description, - Bitmap.createBitmap(mDummyIcon), null, - 0xFF000000 | (0xFFFFFF & new Random().nextInt()), - 0xFF000000 | (0xFFFFFF & new Random().nextInt())); + Bitmap.createBitmap(mDummyIcon), null, + 0xFF000000 | (0xFFFFFF & new Random().nextInt()), + 0xFF000000 | (0xFFFFFF & new Random().nextInt()), + 0, 0); } else { rti.taskDescription = new ActivityManager.TaskDescription(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 67be99e5ccca..88711febe18d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -266,7 +266,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController @Override public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description) { + boolean activityIn, boolean activityOut, String description, boolean isTransient) { mWifiVisible = statusIcon.visible && !mBlockWifi; mWifiStrengthId = statusIcon.icon; mWifiBadgeId = statusIcon.iconOverlay; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index c339da761fc9..a3ef91d95149 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -15,6 +15,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArraySet; import android.view.Gravity; @@ -22,6 +23,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dependency; @@ -71,13 +73,18 @@ public interface StatusBarIconController { } @Override - protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) { - StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked); + protected void onIconAdded(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView v = addIcon(index, slot, blocked, icon); + mDarkIconDispatcher.addDarkReceiver(v); + } + + @Override + protected LayoutParams onCreateLayoutParams() { LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); - mGroup.addView(view, index, lp); - mDarkIconDispatcher.addDarkReceiver(view); + return lp; } @Override @@ -116,11 +123,26 @@ public interface StatusBarIconController { com.android.internal.R.dimen.status_bar_icon_size); } - protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) { - StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked); + protected void onIconAdded(int index, String slot, boolean blocked, + StatusBarIcon icon) { + addIcon(index, slot, blocked, icon); + } + + protected StatusBarIconView addIcon(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); view.set(icon); - mGroup.addView(view, index, new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize)); + mGroup.addView(view, index, onCreateLayoutParams()); + return view; + } + + @VisibleForTesting + protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { + return new StatusBarIconView(mContext, slot, null, blocked); + } + + protected LinearLayout.LayoutParams onCreateLayoutParams() { + return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); } protected void destroy() { 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 5ab99e9e5bce..a456786d712f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -109,13 +109,13 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setWifiIndicators(final boolean enabled, final IconState statusIcon, final IconState qsIcon, final boolean activityIn, final boolean activityOut, - final String description) { + final String description, boolean isTransient) { post(new Runnable() { @Override public void run() { for (SignalCallback callback : mSignalCallbacks) { callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, - description); + description, isTransient); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 882902ee3713..b5d22b1da288 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -49,6 +49,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK public class KeyButtonView extends ImageView implements ButtonInterface { + private final boolean mPlaySounds; private int mContentDescriptionRes; private long mDownTime; private int mCode; @@ -90,6 +91,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0); mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true); + mPlaySounds = a.getBoolean(R.styleable.KeyButtonView_playSound, true); TypedValue value = new TypedValue(); if (a.getValue(R.styleable.KeyButtonView_android_contentDescription, value)) { @@ -241,8 +243,9 @@ public class KeyButtonView extends ImageView implements ButtonInterface { } public void playSoundEffect(int soundConstant) { + if (!mPlaySounds) return; mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser()); - }; + } public void sendEvent(int action, int flags) { sendEvent(action, flags, SystemClock.uptimeMillis()); 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 565756017415..c02ce0ea6335 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -19,12 +19,10 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.content.Intent; import android.telephony.SubscriptionInfo; -import android.view.View; import com.android.settingslib.net.DataUsageController; import com.android.settingslib.wifi.AccessPoint; import com.android.systemui.DemoMode; -import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.util.List; @@ -48,7 +46,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D public interface SignalCallback { default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description) {} + 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, 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 12b70983dba3..2104cb1421aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -150,7 +150,7 @@ public class WifiSignalController extends Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription); callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, - wifiDesc); + wifiDesc, mCurrentState.isTransient); } @Override @@ -170,6 +170,9 @@ public class WifiSignalController extends mWifiTracker.handleBroadcast(intent); updateScoreCacheIfNecessary(previousNetworkKey); + mCurrentState.isTransient = mWifiTracker.state == WifiManager.WIFI_STATE_ENABLING + || mWifiTracker.state == WifiManager.WIFI_AP_STATE_DISABLING + || mWifiTracker.connecting; mCurrentState.enabled = mWifiTracker.enabled; mCurrentState.connected = mWifiTracker.connected; mCurrentState.ssid = mWifiTracker.ssid; @@ -252,6 +255,7 @@ public class WifiSignalController extends static class WifiState extends SignalController.State { String ssid; int badgeEnum; + boolean isTransient; @Override public void copyFrom(State s) { @@ -259,19 +263,23 @@ public class WifiSignalController extends WifiState state = (WifiState) s; ssid = state.ssid; badgeEnum = state.badgeEnum; + isTransient = state.isTransient; } @Override protected void toString(StringBuilder builder) { super.toString(builder); builder.append(',').append("ssid=").append(ssid); + builder.append(',').append("badgeEnum=").append(badgeEnum); + builder.append(',').append("isTransient=").append(isTransient); } @Override public boolean equals(Object o) { return super.equals(o) && Objects.equals(((WifiState) o).ssid, ssid) - && (((WifiState) o).badgeEnum == badgeEnum); + && (((WifiState) o).badgeEnum == badgeEnum) + && (((WifiState) o).isTransient == isTransient); } } } diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index ddd8d7b3002f..8eedf3145e61 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -41,6 +41,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-recyclerview \ android-support-v7-preference \ android-support-v7-appcompat \ + android-support-v7-mediarouter \ android-support-v14-preference \ android-support-v17-leanback diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java new file mode 100644 index 000000000000..ecae39a61c29 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -0,0 +1,97 @@ +/* + * 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.systemui.statusbar.phone; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; +import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.utils.leaks.LeakCheckedTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class StatusBarIconControllerTest extends LeakCheckedTest { + + @Before + public void setup() { + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); + mDependency.injectMockDependency(DarkIconDispatcher.class); + } + + @Test + public void testSetCalledOnAdd_IconManager() { + LinearLayout layout = new LinearLayout(mContext); + TestIconManager manager = new TestIconManager(layout); + StatusBarIcon icon = mock(StatusBarIcon.class); + + manager.onIconAdded(0, "test_slot", false, icon); + verify(manager.mMock).set(eq(icon)); + } + + @Test + public void testSetCalledOnAdd_DarkIconManager() { + LinearLayout layout = new LinearLayout(mContext); + TestDarkIconManager manager = new TestDarkIconManager(layout); + StatusBarIcon icon = mock(StatusBarIcon.class); + + manager.onIconAdded(0, "test_slot", false, icon); + verify(manager.mMock).set(eq(icon)); + } + + private static class TestDarkIconManager extends DarkIconManager { + + private final StatusBarIconView mMock; + + public TestDarkIconManager(LinearLayout group) { + super(group); + mMock = mock(StatusBarIconView.class); + } + + @Override + protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { + return mMock; + } + } + + private static class TestIconManager extends IconManager { + + private final StatusBarIconView mMock; + + public TestIconManager(ViewGroup group) { + super(group); + mMock = mock(StatusBarIconView.class); + } + + @Override + protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { + return mMock; + } + } + +} 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 b544d9df1292..3ed1681644f2 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 @@ -79,7 +79,7 @@ public class CallbackHandlerTest { boolean in = true; boolean out = true; String description = "Test"; - mHandler.setWifiIndicators(enabled, status, qs, in, out, description); + mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true); waitForCallbacks(); ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class); @@ -88,15 +88,17 @@ public class CallbackHandlerTest { ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<Boolean> isTransient = ArgumentCaptor.forClass(Boolean.class); Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(), statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(), - descArg.capture()); + descArg.capture(), isTransient.capture()); assertEquals(enabled, (boolean) enableArg.getValue()); assertEquals(status, statusArg.getValue()); assertEquals(qs, qsArg.getValue()); assertEquals(in, (boolean) inArg.getValue()); assertEquals(out, (boolean) outArg.getValue()); assertEquals(description, descArg.getValue()); + assertTrue(isTransient.getValue()); } @Test 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 efa232bb33af..483a8371b85b 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 @@ -122,7 +122,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(), - any()); + any(), anyBoolean()); IconState iconState = iconArg.getValue(); assertEquals("Badged Wifi Resource is set", @@ -269,7 +269,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( - anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any()); + anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean()); assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue()); assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue()); } @@ -282,7 +282,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( enabledArg.capture(), any(), iconArg.capture(), anyBoolean(), - anyBoolean(), descArg.capture()); + anyBoolean(), descArg.capture(), anyBoolean()); IconState iconState = iconArg.getValue(); assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue()); assertEquals("WiFi connected, in quick settings", connected, iconState.visible); @@ -295,7 +295,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(), - any()); + any(), anyBoolean()); IconState iconState = iconArg.getValue(); assertEquals("WiFi visible, in status bar", visible, iconState.visible); assertEquals("WiFi signal, in status bar", icon, iconState.icon); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index da441f5eaff3..78bc01dc5389 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3188,21 +3188,21 @@ message MetricsEvent { // ACTION: Logged when a provisioning session has completed PROVISIONING_SESSION_COMPLETED = 735; - // ACTION: An app requested the permission READ_PHONE_NUMBER + // ACTION: An app requested the permission READ_PHONE_NUMBERS // PACKAGE: The package name of the app requesting the permission - ACTION_PERMISSION_REQUEST_READ_PHONE_NUMBER = 736; + ACTION_PERMISSION_REQUEST_READ_PHONE_NUMBERS = 736; - // ACTION: An app was granted the permission READ_PHONE_NUMBER + // ACTION: An app was granted the permission READ_PHONE_NUMBERS // PACKAGE: The package name of the app that was granted the permission - ACTION_PERMISSION_GRANT_READ_PHONE_NUMBER = 737; + ACTION_PERMISSION_GRANT_READ_PHONE_NUMBERS = 737; - // ACTION: An app requested the permission READ_PHONE_NUMBER and the request was denied + // ACTION: An app requested the permission READ_PHONE_NUMBERS and the request was denied // PACKAGE: The package name of the app requesting the permission - ACTION_PERMISSION_DENIED_READ_PHONE_NUMBER = 738; + ACTION_PERMISSION_DENIED_READ_PHONE_NUMBERS = 738; - // ACTION: The permission READ_PHONE_NUMBER was revoked for an app + // ACTION: The permission READ_PHONE_NUMBERS was revoked for an app // PACKAGE: The package name of the app the permission was revoked for - ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBER = 739; + ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBERS = 739; // ACTION: QS Brightness Slider (with auto brightness disabled, and VR enabled) // SUBTYPE: slider value diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 6b917b5298de..f6d91f44b2cf 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -153,6 +153,18 @@ message SystemMessage { // Package: android NOTE_SSL_CERT_INFO = 33; + // Warn the user they are approaching their data limit. + // Package: android + NOTE_NET_WARNING = 34; + + // Warn the user they have reached their data limit. + // Package: android + NOTE_NET_LIMIT = 35; + + // Warn the user they have exceeded their data limit. + // Package: android + NOTE_NET_LIMIT_SNOOZED = 36; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 087c24866146..1968d2e925aa 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -585,17 +585,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { result = mEnabledServicesForFeedbackTempList; result.clear(); List<Service> services = userState.mBoundServices; - while (feedbackType != 0) { - final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType)); - feedbackType &= ~feedbackTypeBit; - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - Service service = services.get(i); - // Don't report the UIAutomation (fake service) - if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName) - && (service.mFeedbackType & feedbackTypeBit) != 0) { - result.add(service.mAccessibilityServiceInfo); - } + for (int serviceCount = services.size(), i = 0; i < serviceCount; ++i) { + Service service = services.get(i); + // Don't report the UIAutomation (fake service) + if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName) + && (service.mFeedbackType & feedbackType) != 0) { + result.add(service.mAccessibilityServiceInfo); } } } diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 3aba723b7e5f..20def0c5a4f8 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -156,7 +156,7 @@ final class ViewState { } // Then checks if the session has a response waiting authentication; if so, uses it instead. final FillResponse currentResponse = mSession.getCurrentResponse(); - if (currentResponse.getAuthentication() != null) { + if (currentResponse != null && currentResponse.getAuthentication() != null) { mListener.onFillReady(currentResponse, this.id, mCurrentValue); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 0e752ffdb5a5..25ac008fa586 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -29,13 +29,6 @@ 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.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; -import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; -import static android.net.NetworkPolicyManager.RULE_NONE; -import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; -import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; -import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; -import static android.net.NetworkPolicyManager.uidRulesToString; import android.annotation.Nullable; import android.app.BroadcastOptions; @@ -104,7 +97,6 @@ import android.security.Credentials; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.ArraySet; import android.util.LocalLog; import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; @@ -130,6 +122,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.XmlUtils; +import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.KeepaliveTracker; @@ -147,6 +140,7 @@ import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.android.server.net.BaseNetworkObserver; import com.android.server.net.LockdownVpnTracker; +import com.android.server.net.NetworkPolicyManagerInternal; import com.google.android.collect.Lists; @@ -221,18 +215,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean mLockdownEnabled; private LockdownVpnTracker mLockdownTracker; - /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */ - private Object mRulesLock = new Object(); - /** Currently active network rules by UID. */ - @GuardedBy("mRulesLock") - private SparseIntArray mUidRules = new SparseIntArray(); - /** Set of ifaces that are costly. */ - @GuardedBy("mRulesLock") - private ArraySet<String> mMeteredIfaces = new ArraySet<>(); - /** Flag indicating if background data is restricted. */ - @GuardedBy("mRulesLock") - private boolean mRestrictBackground; - final private Context mContext; private int mNetworkPreference; // 0 is full bad, 100 is full good @@ -246,6 +228,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private INetworkManagementService mNetd; private INetworkStatsService mStatsService; private INetworkPolicyManager mPolicyManager; + private NetworkPolicyManagerInternal mPolicyManagerInternal; private String mCurrentTcpBufferSizes; @@ -715,12 +698,15 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetd = checkNotNull(netManager, "missing INetworkManagementService"); mStatsService = checkNotNull(statsService, "missing INetworkStatsService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); + mPolicyManagerInternal = checkNotNull( + LocalServices.getService(NetworkPolicyManagerInternal.class), + "missing NetworkPolicyManagerInternal"); + mKeyStore = KeyStore.getInstance(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); try { - mPolicyManager.setConnectivityListener(mPolicyListener); - mRestrictBackground = mPolicyManager.getRestrictBackground(); + mPolicyManager.registerListener(mPolicyListener); } catch (RemoteException e) { // ouch, no rules updates means some processes may never get network loge("unable to register INetworkPolicyListener" + e); @@ -991,51 +977,22 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid, boolean ignoreBlocked) { // Networks aren't blocked when ignoring blocked status - if (ignoreBlocked) return false; + if (ignoreBlocked) { + return false; + } // Networks are never blocked for system services - if (isSystem(uid)) return false; - - final boolean networkMetered; - final int uidRules; - + // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked. + if (isSystem(uid)) { + return false; + } synchronized (mVpns) { final Vpn vpn = mVpns.get(UserHandle.getUserId(uid)); if (vpn != null && vpn.isBlockingUid(uid)) { return true; } } - final String iface = (lp == null ? "" : lp.getInterfaceName()); - synchronized (mRulesLock) { - networkMetered = mMeteredIfaces.contains(iface); - uidRules = mUidRules.get(uid, RULE_NONE); - } - - boolean allowed = true; - // Check Data Saver Mode first... - if (networkMetered) { - if ((uidRules & RULE_REJECT_METERED) != 0) { - if (LOGD_RULES) Log.d(TAG, "uid " + uid + " is blacklisted"); - // Explicitly blacklisted. - allowed = false; - } else { - allowed = !mRestrictBackground - || (uidRules & RULE_ALLOW_METERED) != 0 - || (uidRules & RULE_TEMPORARY_ALLOW_METERED) != 0; - if (LOGD_RULES) Log.d(TAG, "allowed status for uid " + uid + " when" - + " mRestrictBackground=" + mRestrictBackground - + ", whitelisted=" + ((uidRules & RULE_ALLOW_METERED) != 0) - + ", tempWhitelist= + ((uidRules & RULE_TEMPORARY_ALLOW_METERED) != 0)" - + ": " + allowed); - } - } - // ...then power restrictions. - if (allowed) { - allowed = (uidRules & RULE_REJECT_ALL) == 0; - if (LOGD_RULES) Log.d(TAG, "allowed status for uid " + uid + " when" - + " rule is " + uidRulesToString(uidRules) + ": " + allowed); - } - return !allowed; + return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface); } private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) { @@ -1481,67 +1438,24 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { + private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { @Override public void onUidRulesChanged(int uid, int uidRules) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")"); - } - - synchronized (mRulesLock) { - // skip update when we've already applied rules - final int oldRules = mUidRules.get(uid, RULE_NONE); - if (oldRules == uidRules) return; - - if (uidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, uidRules); - } - } - // TODO: notify UID when it has requested targeted updates } - @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")"); - } - - synchronized (mRulesLock) { - mMeteredIfaces.clear(); - for (String iface : meteredIfaces) { - mMeteredIfaces.add(iface); - } - } } - @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")"); - } - - synchronized (mRulesLock) { - mRestrictBackground = restrictBackground; - } - + // TODO: relocate this specific callback in Tethering. if (restrictBackground) { log("onRestrictBackgroundChanged(true): disabling tethering"); mTethering.untetherAll(); } } - @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onUidRulesChanged(uid=" + uid + ", uidPolicies=" + uidPolicies + ")"); - } } }; @@ -1976,33 +1890,6 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); pw.println(); - pw.println("Metered Interfaces:"); - pw.increaseIndent(); - for (String value : mMeteredIfaces) { - pw.println(value); - } - pw.decreaseIndent(); - pw.println(); - - pw.print("Restrict background: "); - pw.println(mRestrictBackground); - pw.println(); - - pw.println("Status for known UIDs:"); - pw.increaseIndent(); - final int size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - pw.print("UID="); - pw.print(uid); - final int uidRules = mUidRules.get(uid, RULE_NONE); - pw.print(" rules="); - pw.print(uidRulesToString(uidRules)); - pw.println(); - } - pw.println(); - pw.decreaseIndent(); - pw.println("Network Requests:"); pw.increaseIndent(); for (NetworkRequestInfo nri : mNetworkRequests.values()) { @@ -3437,6 +3324,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.e(TAG, s); } + private static void loge(String s, Throwable t) { + Slog.e(TAG, s, t); + } + private static <T> T checkNotNull(T value, String message) { if (value == null) { throw new NullPointerException(message); @@ -4197,20 +4088,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) { final int uid = Binder.getCallingUid(); if (isSystem(uid)) { + // Exemption for system uid. return; } - // if UID is restricted, don't allow them to bring up metered APNs - if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) == false) { - final int uidRules; - synchronized(mRulesLock) { - uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); - } - if (mRestrictBackground && (uidRules & RULE_ALLOW_METERED) == 0 - && (uidRules & RULE_TEMPORARY_ALLOW_METERED) == 0) { - // we could silently fail or we can filter the available nets to only give - // them those they have access to. Chose the more useful option. - networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); - } + if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + // Policy already enforced. + return; + } + if (mPolicyManagerInternal.isUidRestrictedOnMeteredNetworks(uid)) { + // If UID is restricted, don't allow them to bring up metered APNs. + networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); } } diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 421d5a6ab964..c105b1244e74 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.app.ActivityThread; import android.content.Context; import android.os.IBinder; import android.os.ServiceManager; @@ -104,6 +105,16 @@ public abstract class SystemService { } /** + * Get the system UI context. This context is to be used for displaying UI. It is themable, + * which means resources can be overridden at runtime. Do not use to retrieve properties that + * configure the behavior of the device that is not UX related. + */ + public final Context getUiContext() { + // This has already been set up by the time any SystemServices are created. + return ActivityThread.currentActivityThread().getSystemUiContext(); + } + + /** * Returns true if the system is running in safe mode. * TODO: we should define in which phase this becomes valid */ diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 8b0665c6d312..4cbfb275fd32 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -398,7 +398,7 @@ public final class ActiveServices { r.delayedStop = false; r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - service, neededGrants)); + service, neededGrants, callingUid)); final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; @@ -1140,7 +1140,7 @@ public final class ActiveServices { // Service is already running, so we can immediately // publish the connection. try { - c.conn.connected(s.name, b.intent.binder); + c.conn.connected(s.name, b.intent.binder, false); } catch (Exception e) { Slog.w(TAG, "Failure sending service " + s.shortName + " to connection " + c.conn.asBinder() @@ -1194,7 +1194,7 @@ public final class ActiveServices { } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c); try { - c.conn.connected(r.name, service); + c.conn.connected(r.name, service, false); } catch (Exception e) { Slog.w(TAG, "Failure sending service " + r.name + " to connection " + c.conn.asBinder() + @@ -1355,10 +1355,10 @@ public final class ActiveServices { if (r == null) { try { // TODO: come back and remove this assumption to triage all services - ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service, + ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service, resolvedType, ActivityManagerService.STOCK_PM_FLAGS | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - userId); + userId, callingUid); ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null; if (sInfo == null) { @@ -1927,7 +1927,7 @@ public final class ActiveServices { // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - null, null)); + null, null, 0)); } sendServiceArgsLocked(r, execInFg, true); @@ -1979,7 +1979,8 @@ public final class ActiveServices { mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, si.getUriPermissionsLocked()); } - // TODO b/34123112; Insert ephemeral grant here + mAm.grantEphemeralAccessLocked(r.userId, si.intent, + r.appInfo.uid, UserHandle.getAppId(si.callingId)); bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; @@ -2080,7 +2081,7 @@ public final class ActiveServices { // being brought down. Mark it as dead. cr.serviceDead = true; try { - cr.conn.connected(r.name, null); + cr.conn.connected(r.name, null, true); } catch (Exception e) { Slog.w(TAG, "Failure disconnecting service " + r.name + " to connection " + c.get(i).conn.asBinder() + @@ -2591,7 +2592,7 @@ public final class ActiveServices { stopServiceLocked(sr); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, - sr.makeNextStartId(), baseIntent, null)); + sr.makeNextStartId(), baseIntent, null, 0)); if (sr.app != null && sr.app.thread != null) { // We always run in the foreground, since this is called as // part of the "remove task" UI operation. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c27fa1b849a7..4c747a1c5433 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1350,7 +1350,13 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") boolean mLaunchWarningShown = false; @GuardedBy("this") boolean mCheckedForSetup = false; - Context mContext; + final Context mContext; + + /** + * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can + * change at runtime. Use mContext for non-UI purposes. + */ + final Context mUiContext; /** * The time at which we will allow normal application switches again, @@ -1852,7 +1858,7 @@ public class ActivityManagerService extends IActivityManager.Stub } break; case SHOW_FACTORY_ERROR_UI_MSG: { Dialog d = new FactoryErrorDialog( - mContext, msg.getData().getCharSequence("msg")); + mUiContext, msg.getData().getCharSequence("msg")); d.show(); ensureBootCompleted(); } break; @@ -1863,7 +1869,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!app.waitedForDebugger) { Dialog d = new AppWaitingForDebuggerDialog( ActivityManagerService.this, - mContext, app); + mUiContext, app); app.waitDialog = d; app.waitedForDebugger = true; d.show(); @@ -1878,24 +1884,24 @@ public class ActivityManagerService extends IActivityManager.Stub } break; case SHOW_UID_ERROR_UI_MSG: { if (mShowDialogs) { - AlertDialog d = new BaseErrorDialog(mContext); + AlertDialog d = new BaseErrorDialog(mUiContext); d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.setCancelable(false); - d.setTitle(mContext.getText(R.string.android_system_label)); - d.setMessage(mContext.getText(R.string.system_error_wipe_data)); - d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + d.setTitle(mUiContext.getText(R.string.android_system_label)); + d.setMessage(mUiContext.getText(R.string.system_error_wipe_data)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok), obtainMessage(DISMISS_DIALOG_UI_MSG, d)); d.show(); } } break; case SHOW_FINGERPRINT_ERROR_UI_MSG: { if (mShowDialogs) { - AlertDialog d = new BaseErrorDialog(mContext); + AlertDialog d = new BaseErrorDialog(mUiContext); d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.setCancelable(false); - d.setTitle(mContext.getText(R.string.android_system_label)); - d.setMessage(mContext.getText(R.string.system_error_manufacturer)); - d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + d.setTitle(mUiContext.getText(R.string.android_system_label)); + d.setMessage(mUiContext.getText(R.string.system_error_manufacturer)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok), obtainMessage(DISMISS_DIALOG_UI_MSG, d)); d.show(); } @@ -1919,7 +1925,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (mode == ActivityManager.COMPAT_MODE_DISABLED || mode == ActivityManager.COMPAT_MODE_ENABLED) { mCompatModeDialog = new CompatModeDialog( - ActivityManagerService.this, mContext, + ActivityManagerService.this, mUiContext, ar.info.applicationInfo); mCompatModeDialog.show(); } @@ -1939,7 +1945,7 @@ public class ActivityManagerService extends IActivityManager.Stub ar.packageName)) { // TODO(multi-display): Show dialog on appropriate display. mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog( - ActivityManagerService.this, mContext, ar.info.applicationInfo); + ActivityManagerService.this, mUiContext, ar.info.applicationInfo); mUnsupportedDisplaySizeDialog.show(); } } @@ -2731,6 +2737,7 @@ public class ActivityManagerService extends IActivityManager.Stub public ActivityManagerService(Injector injector) { mInjector = injector; mContext = mInjector.getContext(); + mUiContext = null; GL_ES_VERSION = 0; mActivityStarter = null; mAppErrors = null; @@ -2762,8 +2769,10 @@ public class ActivityManagerService extends IActivityManager.Stub LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY); mInjector = new Injector(); mContext = systemContext; + mFactoryTest = FactoryTest.getMode(); mSystemThread = ActivityThread.currentActivityThread(); + mUiContext = mSystemThread.getSystemUiContext(); Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); @@ -2806,7 +2815,7 @@ public class ActivityManagerService extends IActivityManager.Stub mServices = new ActiveServices(this); mProviderMap = new ProviderMap(this); - mAppErrors = new AppErrors(mContext, this); + mAppErrors = new AppErrors(mUiContext, this); // TODO: Move creation of battery stats service outside of activity manager service. File dataDir = Environment.getDataDirectory(); @@ -2849,7 +2858,7 @@ public class ActivityManagerService extends IActivityManager.Stub mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; - mStackSupervisor = new ActivityStackSupervisor(this); + mStackSupervisor = createStackSupervisor(); mStackSupervisor.onConfigurationChanged(mTempConfig); mKeyguardController = mStackSupervisor.mKeyguardController; mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); @@ -2897,6 +2906,10 @@ public class ActivityManagerService extends IActivityManager.Stub Watchdog.getInstance().addThread(mHandler); } + protected ActivityStackSupervisor createStackSupervisor() { + return new ActivityStackSupervisor(this, mHandler.getLooper()); + } + public void setSystemServiceManager(SystemServiceManager mgr) { mSystemServiceManager = mgr; } @@ -3151,7 +3164,8 @@ public class ActivityManagerService extends IActivityManager.Stub * {@link ActivityStack#setResumedActivityLocked} when an activity is resumed. */ void setResumedActivityUncheckLocked(ActivityRecord r, String reason) { - if (r.task.isApplicationTask()) { + final TaskRecord task = r.getTask(); + if (task.isApplicationTask()) { if (mCurAppTimeTracker != r.appTimeTracker) { // We are switching app tracking. Complete the current one. if (mCurAppTimeTracker != null) { @@ -3174,17 +3188,18 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null // TODO: Probably not, because we don't want to resume voice on switching // back to this activity - if (r.task.voiceInteractor != null) { - startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid); + if (task.voiceInteractor != null) { + startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid); } else { finishRunningVoiceLocked(); if (mLastResumedActivity != null) { final IVoiceInteractionSession session; - if (mLastResumedActivity.task != null - && mLastResumedActivity.task.voiceSession != null) { - session = mLastResumedActivity.task.voiceSession; + final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask(); + if (lastResumedActivityTask != null + && lastResumedActivityTask.voiceSession != null) { + session = lastResumedActivityTask.voiceSession; } else { session = mLastResumedActivity.voiceSession; } @@ -3317,7 +3332,7 @@ public class ActivityManagerService extends IActivityManager.Stub final void showAskCompatModeDialogLocked(ActivityRecord r) { Message msg = Message.obtain(); msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG; - msg.obj = r.task.askedCompatMode ? null : r; + msg.obj = r.getTask().askedCompatMode ? null : r; mUiHandler.sendMessage(msg); } @@ -4723,7 +4738,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } - if (mRunningVoice != null || activity.task.voiceSession != null + if (mRunningVoice != null || activity.getTask().voiceSession != null || activity.voiceSession != null) { Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); return; @@ -5033,7 +5048,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } // Keep track of the root activity of the task before we finish it - TaskRecord tr = r.task; + TaskRecord tr = r.getTask(); ActivityRecord rootR = tr.getRootActivity(); if (rootR == null) { Slog.w(TAG, "Finishing task with all activities already finished"); @@ -5170,7 +5185,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps // can finish. - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV && mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) { mStackSupervisor.showLockTaskToast(); @@ -7834,7 +7849,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } // An activity is consider to be in multi-window mode if its task isn't fullscreen. - return !r.task.mFullscreen; + return !r.getTask().mFullscreen; } } finally { Binder.restoreCallingIdentity(origId); @@ -9927,8 +9942,9 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.setTaskDescription(td); - r.task.updateTaskDescription(); - mTaskChangeNotificationController.notifyTaskDescriptionChanged(r.task.taskId, td); + final TaskRecord task = r.getTask(); + task.updateTaskDescription(); + mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td); } } } @@ -10378,8 +10394,8 @@ public class ActivityManagerService extends IActivityManager.Stub } if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r); - r.task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, - ANIMATE, !DEFER_RESUME, "exitFreeformMode"); + r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, + REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10760,7 +10776,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task != null) { startLockTaskModeLocked(task); } @@ -10859,7 +10875,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - mStackSupervisor.showLockTaskEscapeMessageLocked(r.task); + mStackSupervisor.showLockTaskEscapeMessageLocked(r.getTask()); } } @@ -11516,6 +11532,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } checkTime(startTime, "getContentProviderImpl: done!"); + + grantEphemeralAccessLocked(userId, null /*intent*/, + cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid())); } // Wait for the provider to be published... @@ -12291,13 +12310,17 @@ public class ActivityManagerService extends IActivityManager.Stub mRecentTasks.notifyTaskPersisterLocked(task, flush); } - /** Notifies all listeners when the pinned stack animation starts. */ + /** + * Notifies all listeners when the pinned stack animation starts. + */ @Override public void notifyPinnedStackAnimationStarted() { mTaskChangeNotificationController.notifyPinnedStackAnimationStarted(); } - /** Notifies all listeners when the pinned stack animation ends. */ + /** + * Notifies all listeners when the pinned stack animation ends. + */ @Override public void notifyPinnedStackAnimationEnded() { mTaskChangeNotificationController.notifyPinnedStackAnimationEnded(); @@ -13114,9 +13137,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return false; } - int index = r.task.mActivities.lastIndexOf(r); + final TaskRecord task = r.getTask(); + int index = task.mActivities.lastIndexOf(r); if (index > 0) { - ActivityRecord under = r.task.mActivities.get(index - 1); + ActivityRecord under = task.mActivities.get(index - 1); under.returningOptions = ActivityOptions.fromBundle(options); } final boolean translucentChanged = r.changeWindowTranslucency(false); @@ -13403,7 +13427,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { throw new IllegalArgumentException(); } - return r.task.getTopActivity() == r; + return r.getTask().getTopActivity() == r; } } @@ -15921,8 +15945,9 @@ public class ActivityManagerService extends IActivityManager.Stub } needSep = true; synchronized (this) { - if (lastTask != r.task) { - lastTask = r.task; + final TaskRecord task = r.getTask(); + if (lastTask != task) { + lastTask = task; pw.print("TASK "); pw.print(lastTask.affinity); pw.print(" id="); pw.print(lastTask.taskId); pw.print(" userId="); pw.println(lastTask.userId); @@ -17636,7 +17661,6 @@ public class ActivityManagerService extends IActivityManager.Stub */ private final boolean cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, boolean allowRestart, int index, boolean replacingPid) { - Slog.d(TAG, "cleanUpApplicationRecord -- " + app.pid); if (index >= 0) { removeLruProcessLocked(app); ProcessList.remove(app.pid); @@ -19784,6 +19808,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * NOTE: For the pinned stack, this method is only called after the bounds animation has + * animated the stack to the fullscreen. + */ @Override public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()"); @@ -20551,8 +20579,9 @@ public class ActivityManagerService extends IActivityManager.Stub app.cached = false; app.empty = false; foregroundActivities = true; - if (r.task != null && minLayer > 0) { - final int layer = r.task.mLayerRank; + final TaskRecord task = r.getTask(); + if (task != null && minLayer > 0) { + final int layer = task.mLayerRank; if (layer >= 0 && minLayer > layer) { minLayer = layer; } @@ -23873,7 +23902,7 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean updateFrameworkRes = packagesToUpdate.contains("android"); for (int i = mLruProcesses.size() - 1; i >= 0; i--) { final ProcessRecord app = mLruProcesses.get(i); - if (app.thread == null || app.pid == Process.myPid()) { + if (app.thread == null) { continue; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 47e11b117d38..3a2941467cb6 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -229,7 +229,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private int theme; // resource identifier of activity's theme. private int realTheme; // actual theme resource we will use, never 0. private int windowFlags; // custom window flags for preview window. - TaskRecord task; // the task this is in. + private TaskRecord task; // the task this is in. private long createTime = System.currentTimeMillis(); long displayStartTime; // when we started launching this activity long fullyDrawnStartTime; // when we started launching this activity @@ -284,6 +284,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. boolean immersive; // immersive mode (don't interrupt if possible) boolean forceNewConfig; // force re-create with new config next time + private boolean mInMultiWindowMode; // whether or not this activity is currently in multi-window + // mode (default false) + private boolean mInPictureInPictureMode; // whether or not this activity is currently in + // picture-in-picture mode (default false) boolean supportsPictureInPictureWhilePausing; // This flag is set by the system to indicate // that the activity can enter picture in picture while pausing (ie. only when another // task is brought to front or started) @@ -615,25 +619,51 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - void scheduleMultiWindowModeChanged() { + void updateMultiWindowMode() { if (task == null || task.getStack() == null || app == null || app.thread == null) { return; } + + // An activity is considered to be in multi-window mode if its task isn't fullscreen. + final boolean inMultiWindowMode = !task.mFullscreen; + if (inMultiWindowMode != mInMultiWindowMode) { + mInMultiWindowMode = inMultiWindowMode; + scheduleMultiWindowModeChanged(getConfiguration()); + } + } + + private void scheduleMultiWindowModeChanged(Configuration overrideConfig) { try { - // An activity is considered to be in multi-window mode if its task isn't fullscreen. - app.thread.scheduleMultiWindowModeChanged(appToken, !task.mFullscreen); + app.thread.scheduleMultiWindowModeChanged(appToken, mInMultiWindowMode, + overrideConfig); } catch (Exception e) { // If process died, I don't care. } } - void schedulePictureInPictureModeChanged() { + void updatePictureInPictureMode(Rect targetStackBounds) { if (task == null || task.getStack() == null || app == null || app.thread == null) { return; } + + final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) && + (targetStackBounds != null); + if (inPictureInPictureMode != mInPictureInPictureMode) { + // Picture-in-picture mode changes also trigger a multi-window mode change as well, so + // update that here in order + mInPictureInPictureMode = inPictureInPictureMode; + mInMultiWindowMode = inPictureInPictureMode; + final Configuration newConfig = task.computeNewOverrideConfigurationForBounds( + targetStackBounds, null); + schedulePictureInPictureModeChanged(newConfig); + scheduleMultiWindowModeChanged(newConfig); + } + } + + private void schedulePictureInPictureModeChanged(Configuration overrideConfig) { try { - app.thread.schedulePictureInPictureModeChanged( - appToken, task.getStackId() == PINNED_STACK_ID); + app.thread.schedulePictureInPictureModeChanged(appToken, mInPictureInPictureMode, + overrideConfig); } catch (Exception e) { // If process died, no one cares. } @@ -656,9 +686,48 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo @Override protected ConfigurationContainer getParent() { + return getTask(); + } + + TaskRecord getTask() { return task; } + /** + * Sets reference to the {@link TaskRecord} the {@link ActivityRecord} will treat as its parent. + * Note that this does not actually add the {@link ActivityRecord} as a {@link TaskRecord} + * children. However, this method will clean up references to this {@link ActivityRecord} in + * {@link ActivityStack}. + * @param task The new parent {@link TaskRecord}. + */ + void setTask(TaskRecord task) { + setTask(task, false /*reparenting*/); + } + + /** + * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}. + */ + void setTask(TaskRecord task, boolean reparenting) { + // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}. + if (task != null && task == getTask()) { + return; + } + + final ActivityStack stack = getStack(); + + // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this + // {@link ActivityRecord} from its current {@link ActivityStack}. + if (!reparenting && stack != null && (task == null || stack != task.getStack())) { + stack.onActivityRemovedFromStack(this); + } + + this.task = task; + + if (!reparenting) { + onParentChanged(); + } + } + static class Token extends IApplicationToken.Stub { private final WeakReference<ActivityRecord> weakActivity; @@ -895,8 +964,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Must reparent first in window manager mWindowContainerController.reparent(newTask.getWindowContainerController(), position); - // Remove the activity from the old task and add it to the new task - prevTask.removeActivity(this); + // Remove the activity from the old task and add it to the new task. + prevTask.removeActivity(this, true /*reparenting*/); newTask.addActivityAtIndex(position, this); } @@ -2122,23 +2191,31 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return true; } - /** Computes the override configuration for this activity */ + /** + * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}. + */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void computeBounds(Rect outBounds) { outBounds.setEmpty(); final float maxAspectRatio = info.maxAspectRatio; final ActivityStack stack = getStack(); - if ((task != null && !task.mFullscreen) || maxAspectRatio == 0 || stack == null) { + if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0) { // We don't set override configuration if that activity task isn't fullscreen. I.e. the // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for - // the activity. + // the activity. This is indicated by an empty {@link outBounds}. return; } - stack.getDisplaySize(mTmpPoint); - int maxActivityWidth = mTmpPoint.x; - int maxActivityHeight = mTmpPoint.y; - if (mTmpPoint.x < mTmpPoint.y) { + // We must base this on the parent configuration, because we set our override + // configuration's appBounds based on the result of this method. If we used our own + // configuration, it would be influenced by past invocations. + final Configuration configuration = getParent().getConfiguration(); + final int containingAppWidth = configuration.appBounds.width(); + final int containingAppHeight = configuration.appBounds.height(); + int maxActivityWidth = containingAppWidth; + int maxActivityHeight = containingAppHeight; + + if (containingAppWidth < containingAppHeight) { // Width is the shorter side, so we use that to figure-out what the max. height should // be given the aspect ratio. maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f); @@ -2148,8 +2225,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f); } - if (mTmpPoint.x <= maxActivityWidth && mTmpPoint.y <= maxActivityHeight) { + if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) { // The display matches or is less than the activity aspect ratio, so nothing else to do. + // Return the existing bounds. If this method is running for the first time, + // {@link mBounds} will be empty (representing no override). If the method has run + // before, then effect of {@link mBounds} will already have been applied to the + // value returned from {@link getConfiguration}. Refer to + // {@link TaskRecord#computeOverrideConfiguration}. + outBounds.set(mBounds); return; } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 498de63d11d8..f13b11e65a88 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -116,6 +116,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -342,7 +343,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private final Rect mTmpRect2 = new Rect(); /** Run all ActivityStacks through this */ - private final ActivityStackSupervisor mStackSupervisor; + protected final ActivityStackSupervisor mStackSupervisor; private final LaunchingTaskPositioner mTaskPositioner; @@ -725,7 +726,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r == null) { return null; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { if (stack != this) Slog.w(TAG, @@ -934,7 +935,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls=" + taskIntent.getComponent().flattenToShortString() - + "/aff=" + r.task.rootAffinity + " to new cls=" + + "/aff=" + r.getTask().rootAffinity + " to new cls=" + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity); // TODO Refactor to remove duplications. Check if logic can be simplified. if (taskIntent != null && taskIntent.getComponent() != null && @@ -1049,8 +1050,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai void addRecentActivityLocked(ActivityRecord r) { if (r != null) { - mRecentTasks.addLocked(r.task); - r.task.touchActiveTime(); + final TaskRecord task = r.getTask(); + mRecentTasks.addLocked(task); + task.touchActiveTime(); } } @@ -1226,11 +1228,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null; prev.state = ActivityState.PAUSING; - prev.task.touchActiveTime(); + prev.getTask().touchActiveTime(); clearLaunchTime(prev); final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(); if (mService.mHasRecents - && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) { + && (next == null || next.noDisplay || next.getTask() != prev.getTask() + || uiSleeping)) { prev.mUpdateTaskThumbnailWhenHidden = true; } stopFullyDrawnTraceIfNeeded(); @@ -1457,7 +1460,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Find the first visible activity above the passed activity and if it is translucent return it // otherwise return null; ActivityRecord findNextTranslucentActivity(ActivityRecord r) { - TaskRecord task = r.task; + TaskRecord task = r.getTask(); if (task == null) { return null; } @@ -1604,7 +1607,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Otherwise, the docked stack is always visible, except in the case where the top // running activity task in the focus stack doesn't support any form of resizing but we // show it for the home task even though it's not resizable. - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE : STACK_INVISIBLE; } @@ -2157,8 +2160,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity = r; r.state = ActivityState.RESUMED; mService.setResumedActivityUncheckLocked(r, reason); - r.task.touchActiveTime(); - mRecentTasks.addLocked(r.task); + final TaskRecord task = r.getTask(); + task.touchActiveTime(); + mRecentTasks.addLocked(task); } private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { @@ -2207,8 +2211,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - final TaskRecord nextTask = next.task; - final TaskRecord prevTask = prev != null ? prev.task : null; + final TaskRecord nextTask = next.getTask(); + final TaskRecord prevTask = prev != null ? prev.getTask() : null; if (prevTask != null && prevTask.getStack() == this && prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); @@ -2373,7 +2377,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE : TRANSIT_TASK_CLOSE, false); } @@ -2385,7 +2389,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND @@ -2522,7 +2526,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.notifyAppResumed(next.stopped, allowSavedSurface); EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, - System.identityHashCode(next), next.task.taskId, next.shortComponentName); + System.identityHashCode(next), next.getTask().taskId, + next.shortComponentName); next.sleeping = false; mService.showUnsupportedZoomDialogIfNeededLocked(next); @@ -2696,9 +2701,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - // If the task was launched from the assistant stack, set the return type to assistant final ActivityStack lastStack = mStackSupervisor.getLastStack(); - if (lastStack != null && lastStack.isAssistantStack()) { + + // If there is no last task, do not set task to return to + if (lastStack == null) { + return; + } + + // If the task was launched from the assistant stack, set the return type to assistant + if (lastStack.isAssistantStack()) { task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE); return; } @@ -2721,7 +2732,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { - TaskRecord rTask = r.task; + TaskRecord rTask = r.getTask(); final int taskId = rTask.taskId; // mLaunchTaskBehind tasks get placed at the back of the task stack. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { @@ -2740,7 +2751,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // All activities in task are finishing. continue; } - if (task == r.task) { + if (task == rTask) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. @@ -2762,13 +2773,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If we are not placing the new activity frontmost, we do not want to deliver the // onUserLeaving callback to the actual frontmost activity - if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { + final TaskRecord activityTask = r.getTask(); + if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { mStackSupervisor.mUserLeaving = false; if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, "startActivity() behind front, mUserLeaving=false"); } - task = r.task; + task = activityTask; // Slot the activity into the history stack and proceed if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, @@ -2795,7 +2807,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } else { // If a new task is being launched, then mark the existing top activity as // supporting picture-in-picture while pausing - if (focusedTopActivity != null) { + if (focusedTopActivity != null && + focusedTopActivity.getStack().getStackId() != PINNED_STACK_ID) { focusedTopActivity.supportsPictureInPictureWhilePausing = true; } transit = TRANSIT_TASK_OPEN; @@ -2829,11 +2842,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // "has the same starting icon" as the next one. This allows the // window manager to keep the previous window it had previously // created, if it still had one. - ActivityRecord prev = r.task.topRunningActivityWithStartingWindowLocked(); + TaskRecord prevTask = r.getTask(); + ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked(); if (prev != null) { // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. - if (prev.task != r.task) { + if (prev.getTask() != prevTask) { prev = null; } // (2) The current activity is already displayed. @@ -2852,7 +2866,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) { - return topFocusedActivity != null && r.task != topFocusedActivity.task; + return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask(); } /** @@ -2919,20 +2933,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null - && target.taskAffinity.equals(bottom.task.affinity)) { + && target.taskAffinity.equals(bottom.getTask().affinity)) { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. - targetTask = bottom.task; + targetTask = bottom.getTask(); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to bottom task " + bottom.task); + + " out to bottom task " + targetTask); } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.userId), target.info, null, null, null, false, target.mActivityType); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to new task " + target.task); + + " out to new task " + targetTask); } boolean noOptions = canMoveOptions; @@ -2954,7 +2968,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai "Removing activity " + p + " from task=" + task + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4)); if (DEBUG_TASKS) Slog.v(TAG_TASKS, - "Pushing next activity " + p + " out to target's task " + target.task); + "Pushing next activity " + p + " out to target's task " + target); p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded"); } @@ -3125,13 +3139,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean forceReset = (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; if (ACTIVITY_INACTIVE_RESET_TIME > 0 - && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { + && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { forceReset = true; } } - final TaskRecord task = taskTop.task; + final TaskRecord task = taskTop.getTask(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ @@ -3214,7 +3228,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stack as long as there is a running activity. return; } else { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() || task.isOverAssistantStack(); if (r.frontOfTask && task == topTask() && @@ -3373,10 +3387,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - int taskNdx = mTaskHistory.indexOf(r.task); - int activityNdx = r.task.mActivities.indexOf(r); + finishedTask = r.getTask(); + int taskNdx = mTaskHistory.indexOf(finishedTask); + final TaskRecord task = finishedTask; + int activityNdx = task.mActivities.indexOf(r); finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - finishedTask = r.task; + finishedTask = task; // Also terminate any activities below it that aren't yet // stopped, to avoid a situation where one will get // re-start our crashing activity once it gets resumed again. @@ -3446,7 +3462,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean finishActivityAffinityLocked(ActivityRecord r) { - ArrayList<ActivityRecord> activities = r.task.mActivities; + ArrayList<ActivityRecord> activities = r.getTask().mActivities; for (int index = activities.indexOf(r); index >= 0; --index) { ActivityRecord cur = activities.get(index); if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) { @@ -3511,7 +3527,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mWindowManager.deferSurfaceLayout(); try { r.makeFinishingLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), task.taskId, r.shortComponentName, reason); @@ -3700,23 +3716,24 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - if (srec == null || srec.task.affinity == null || - !srec.task.affinity.equals(destAffinity)) { + if (srec == null || srec.getTask().affinity == null || + !srec.getTask().affinity.equals(destAffinity)) { return true; } // Document-centric case: an app may be split in to multiple documents; // they need to re-create their task if this current activity is the root // of a document, unless simply finishing it will return them to the the // correct app behind. - if (srec.frontOfTask && srec.task != null && srec.task.getBaseIntent() != null - && srec.task.getBaseIntent().isDocument()) { + final TaskRecord task = srec.getTask(); + if (srec.frontOfTask && task != null && task.getBaseIntent() != null + && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... - if (srec.task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { + if (task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { // Finishing won't return to an application, so we need to recreate. return true; } // We now need to get the task below it to determine what to do. - int taskIdx = mTaskHistory.indexOf(srec.task); + int taskIdx = mTaskHistory.indexOf(task); if (taskIdx <= 0) { Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec); return false; @@ -3726,7 +3743,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } TaskRecord prevTask = mTaskHistory.get(taskIdx); - if (!srec.task.affinity.equals(prevTask.affinity)) { + if (!task.affinity.equals(prevTask.affinity)) { // These are different apps, so need to recreate. return true; } @@ -3736,7 +3753,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode, Intent resultData) { - final TaskRecord task = srec.task; + final TaskRecord task = srec.getTask(); final ArrayList<ActivityRecord> activities = task.mActivities; final int start = activities.indexOf(srec); if (!mTaskHistory.contains(task) || (start < 0)) { @@ -3815,6 +3832,22 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Binder.restoreCallingIdentity(origId); return foundParentInTask; } + + /** + * Remove any state associated with the {@link ActivityRecord}. This should be called whenever + * an activity moves away from the stack. + */ + void onActivityRemovedFromStack(ActivityRecord r) { + if (mResumedActivity == r) { + mResumedActivity = null; + } + if (mPausingActivity == r) { + mPausingActivity = null; + } + + removeTimeoutsForActivityLocked(r); + } + /** * Perform the common clean-up of an activity record. This is called both * as part of destroyActivityLocked() (when destroying the client-side @@ -3825,12 +3858,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * Note: Call before #removeActivityFromHistoryLocked. */ private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) { - if (mResumedActivity == r) { - mResumedActivity = null; - } - if (mPausingActivity == r) { - mPausingActivity = null; - } + onActivityRemovedFromStack(r); r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; @@ -3897,7 +3925,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r); r.app = null; r.removeWindowContainer(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean lastActivity = task != null ? task.removeActivity(r) : false; // If we are removing the last activity in the task, not including task overlay activities, // then fall through into the block below to remove the entire task itself @@ -4043,7 +4071,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + ", app=" + (r.app != null ? r.app.processName : "(null)")); EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, reason); + r.getTask().taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -4275,7 +4303,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, + r.getTask().taskId, r.shortComponentName, "proc died without state saved"); if (r.state == ActivityState.RESUMED) { mService.updateUsageStats(r, false); @@ -4403,7 +4431,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // If a new task is moved to the front, then mark the existing top activity as supporting // picture-in-picture while paused - if (topActivity != null) { + if (topActivity != null && topActivity.getStack().getStackId() != PINNED_STACK_ID) { topActivity.supportsPictureInPictureWhilePausing = true; } @@ -4529,7 +4557,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null; + final TaskRecord task = mResumedActivity != null ? mResumedActivity.getTask() : null; if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! @@ -4570,7 +4598,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - final TaskRecord startTask = start.task; + final TaskRecord startTask = start.getTask(); boolean behindFullscreen = false; boolean updatedConfig = false; @@ -4578,7 +4606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final TaskRecord task = mTaskHistory.get(taskIndex); final ArrayList<ActivityRecord> activities = task.mActivities; int activityIndex = - (start.task == task) ? activities.indexOf(start) : activities.size() - 1; + (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1; for (; activityIndex >= 0; --activityIndex) { final ActivityRecord r = activities.get(activityIndex); updatedConfig |= r.ensureActivityConfigurationLocked(0 /* globalChanges */, @@ -4739,7 +4767,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || filterByClasses.contains(r.realActivity.getClassName()))) || (packageName == null && r.userId == userId); if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (sameComponent || r.task == lastTask) + && (sameComponent || r.getTask() == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { if (!doit) { if (r.finishing) { @@ -4765,7 +4793,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } r.app = null; } - lastTask = r.task; + lastTask = r.getTask(); if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", true)) { // r has been deleted from mActivities, accommodate. @@ -4816,7 +4844,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_ALL) Slog.v( TAG, r.intent.getComponent().flattenToShortString() - + ": task=" + r.task); + + ": task=" + r.getTask()); } RunningTaskInfo ci = new RunningTaskInfo(); @@ -4832,8 +4860,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai topTask = false; } - if (top.task != null) { - ci.description = top.task.lastDescription; + if (top.getTask() != null) { + ci.description = top.getTask().lastDescription; } ci.numActivities = numActivities; ci.numRunning = numRunning; @@ -4982,9 +5010,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.removeWindowContainer(); } - final ActivityRecord r = mResumedActivity; - if (r != null && r.task == task) { - mResumedActivity = null; + for (ActivityRecord record : task.mActivities) { + onActivityRemovedFromStack(record); } final int taskNdx = mTaskHistory.indexOf(task); @@ -5074,7 +5101,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } void addTask(final TaskRecord task, final boolean toTop, String reason) { - addTask(task, toTop ? MAX_VALUE : 0, reason); + addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason); if (toTop) { // TODO: figure-out a way to remove this call. mWindowContainerController.positionChildAtTop(task.getWindowContainerController(), @@ -5084,7 +5111,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: This shouldn't allow automatic reparenting. Remove the call to preAddTask and deal // with the fall-out... - void addTask(final TaskRecord task, int position, String reason) { + void addTask(final TaskRecord task, int position, boolean schedulePictureInPictureModeChange, + String reason) { // TODO: Is this remove really needed? Need to look into the call path for the other addTask mTaskHistory.remove(task); position = getAdjustedPositionForTask(task, position, null /* starting */); @@ -5100,7 +5128,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai updateTaskMovement(task, toTop); - postAddTask(task, prevStack); + postAddTask(task, prevStack, schedulePictureInPictureModeChange); } void positionChildAt(TaskRecord task, int index) { @@ -5116,7 +5144,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity; insertTaskAtPosition(task, index); task.setStack(this); - postAddTask(task, null /* prevStack */); + postAddTask(task, null /* prevStack */, true /* schedulePictureInPictureModeChange */); if (wasResumed) { if (mResumedActivity != null) { @@ -5142,9 +5170,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return prevStack; } - private void postAddTask(TaskRecord task, ActivityStack prevStack) { - if (prevStack != null) { - mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack); + /** + * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode + * change. Callers may set this to false if they are explicitly scheduling PiP mode + * changes themselves, like during the PiP animation + */ + private void postAddTask(TaskRecord task, ActivityStack prevStack, + boolean schedulePictureInPictureModeChange) { + if (schedulePictureInPictureModeChange && prevStack != null) { + mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, prevStack); } else if (task.voiceSession != null) { try { task.voiceSession.taskStarted(task.intent, task.taskId); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c65ca798a7b0..b72cd73e85cf 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -164,6 +164,7 @@ import android.view.Display; import android.view.InputEvent; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -173,6 +174,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.StackWindowController; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; @@ -371,6 +373,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * application */ final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>(); + /** The target stack bounds for the picture-in-picture mode changed that we need to report to + * the application */ + Rect mPipModeChangedTargetStackBounds; + /** Used on user changes */ final ArrayList<UserState> mStartingUsers = new ArrayList<>(); @@ -549,9 +555,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - public ActivityStackSupervisor(ActivityManagerService service) { + public ActivityStackSupervisor(ActivityManagerService service, Looper looper) { mService = service; - mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); + mHandler = new ActivityStackSupervisorHandler(looper); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); mKeyguardController = new KeyguardController(service, this); } @@ -718,7 +724,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (prev != null) { - prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); + prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); } mHomeStack.moveHomeStackTaskToTop(); @@ -1310,7 +1316,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) { setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false); @@ -2334,6 +2340,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID); final boolean isFullscreenStackVisible = fullscreenStack != null && fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE; + // If we are moving from the pinned stack, then the animation takes care of updating + // the picture-in-picture mode. + final boolean schedulePictureInPictureModeChange = (fromStackId != PINNED_STACK_ID); final ArrayList<TaskRecord> tasks = stack.getAllTasks(); final int size = tasks.size(); if (onTop) { @@ -2352,6 +2361,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Defer resume until all the tasks have been moved to the fullscreen stack task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME, + schedulePictureInPictureModeChange, "moveTasksToFullscreenStack - onTop"); } } else { @@ -2361,8 +2371,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ? 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, - REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, - DEFER_RESUME, "moveTasksToFullscreenStack - NOT_onTop"); + REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, + schedulePictureInPictureModeChange, + "moveTasksToFullscreenStack - NOT_onTop"); } } @@ -2506,9 +2517,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE; for (int i = 0; i < tasks.size(); i++) { // Insert the task either at the top of the fullscreen stack if it is hidden, - // or just under the top task if it is currently visible - final int insertPosition = isFullscreenStackVisible - ? Math.max(0, fullscreenStack.getChildCount() - 1) + // or to the bottom if it is currently visible + final int insertPosition = isFullscreenStackVisible ? 0 : fullscreenStack.getChildCount(); final TaskRecord task = tasks.get(i); // Defer resume until we remove all the tasks @@ -2614,7 +2624,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } for (int k = 0; k < proc.activities.size(); k++) { - TaskRecord otherTask = proc.activities.get(k).task; + TaskRecord otherTask = proc.activities.get(k).getTask(); if (tr.taskId != otherTask.taskId && otherTask.inRecents) { // Don't kill process(es) that has an activity in a different task that is // also in recents. @@ -2829,7 +2839,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); try { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (r == task.getStack().getVisibleBehindActivity()) { // An activity can't be pinned and visible behind at the same time. Go ahead and @@ -2856,9 +2866,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // was launched from home so home should be visible behind it. moveHomeStackToFront(reason); } - // Defer resume until below + // Defer resume until below, and do not schedule PiP changes until we animate below task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, - DEFER_RESUME, reason); + DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); } else { // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. @@ -2873,9 +2883,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.mActivityType); r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); - // Defer resume until below + // Defer resume until below, and do not schedule PiP changes until we animate below newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, - DEFER_RESUME, reason); + DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); } // Reset the state that indicates it can enter PiP while pausing after we've moved it @@ -2902,7 +2912,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack == null) { Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: r=" @@ -3195,7 +3205,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = task.getStack(); r.mLaunchTaskBehind = false; @@ -3208,7 +3218,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // task has been shown briefly final ActivityRecord top = stack.topActivity(); if (top != null) { - top.task.touchActiveTime(); + top.getTask().touchActiveTime(); } } @@ -3307,17 +3317,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; } - if (r.task != null) { - if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + r.task + + final TaskRecord task = r.getTask(); + if (task != null) { + if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task + " from " + r); if (firstTask == null) { - firstTask = r.task; - } else if (firstTask != r.task) { + firstTask = task; + } else if (firstTask != task) { if (tasks == null) { tasks = new ArraySet<>(); tasks.add(firstTask); } - tasks.add(r.task); + tasks.add(task); } } } @@ -3656,8 +3668,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.println(header2); header2 = null; } - if (lastTask != r.task) { - lastTask = r.task; + if (lastTask != r.getTask()) { + lastTask = r.getTask(); pw.print(prefix); pw.print(full ? "* " : " "); pw.println(lastTask); @@ -4072,7 +4084,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } final ActivityRecord r = topRunningActivityLocked(); - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; if (mLockTaskModeTasks.isEmpty() && task != null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. @@ -4109,7 +4121,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mActivityMetricsLogger.logWindowState(); } - void scheduleReportMultiWindowModeChanged(TaskRecord task) { + void scheduleUpdateMultiWindowMode(TaskRecord task) { 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) { @@ -4122,22 +4134,39 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void scheduleReportPictureInPictureModeChangedIfNeeded(TaskRecord task, ActivityStack prevStack) { + void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) { final ActivityStack stack = task.getStack(); if (prevStack == null || prevStack == stack || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) { 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) { - mPipModeChangedActivities.add(r); + scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */); + } + + void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds, + boolean immediate) { + + if (immediate) { + mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG); + 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) { + r.updatePictureInPictureMode(targetStackBounds); + } } - } + } else { + 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) { + mPipModeChangedActivities.add(r); + } + } + mPipModeChangedTargetStackBounds = targetStackBounds; - if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) { - mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG); + if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) { + mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG); + } } } @@ -4165,7 +4194,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i); - r.scheduleMultiWindowModeChanged(); + r.updateMultiWindowMode(); } } } break; @@ -4173,7 +4202,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mPipModeChangedActivities.remove(i); - r.schedulePictureInPictureModeChanged(); + r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds); } } } break; @@ -4365,19 +4394,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { mStackId = stackId; mActivityDisplay = activityDisplay; - switch (mStackId) { - case PINNED_STACK_ID: - new PinnedActivityStack(this, mRecentTasks, onTop); - break; - default: - new ActivityStack(this, mRecentTasks, onTop); - break; - } mIdString = "ActivtyContainer{" + mStackId + "}"; + + createStack(stackId, onTop); if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this); } } + protected void createStack(int stackId, boolean onTop) { + switch (stackId) { + case PINNED_STACK_ID: + new PinnedActivityStack(this, mRecentTasks, onTop); + break; + default: + new ActivityStack(this, mRecentTasks, onTop); + break; + } + } + /** * Adds the stack to specified display. Also calls WindowManager to do the same from * {@link ActivityStack#reparent(ActivityDisplay, boolean)}. @@ -4901,7 +4935,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT, - sourceRecord != null ? sourceRecord.task.getStackId() : INVALID_STACK_ID, + sourceRecord != null ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID, sourceRecord, task.getStack()); return ActivityManager.START_TASK_TO_FRONT; } diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 547161ac4169..cafc4f0ecc96 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -188,10 +188,10 @@ class ActivityStartInterceptor { } ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity(); - if (homeActivityRecord != null && homeActivityRecord.task != null) { + if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { // Showing credential confirmation activity in home task to avoid stopping multi-windowed // mode after showing the full-screen credential confirmation activity. - mActivityOptions.setLaunchTaskId(homeActivityRecord.task.taskId); + mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); } final UserInfo parent = mUserManager.getProfileParent(mUserId); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 18e74080490d..b4085697f2da 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -79,7 +79,6 @@ import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS; @@ -87,8 +86,6 @@ import static com.android.server.am.EventLogTags.AM_NEW_INTENT; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; -import static java.lang.Integer.MAX_VALUE; - import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -335,7 +332,7 @@ class ActivityStarter { } if (err == ActivityManager.START_SUCCESS && sourceRecord != null - && sourceRecord.task.voiceSession != null) { + && sourceRecord.getTask().voiceSession != null) { // If this activity is being launched as part of a voice session, we need // to ensure that it is safe to do so. If the upcoming activity will also // be part of the voice session, we can only launch it if it has explicitly @@ -575,7 +572,7 @@ class ActivityStarter { // visibility instead of using this flag. final boolean noDisplayActivityOverHome = sourceRecord != null && sourceRecord.noDisplay - && sourceRecord.task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE; + && sourceRecord.getTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE; if (startedActivityStackId == DOCKED_STACK_ID && (prevFocusedStackId == HOME_STACK_ID || noDisplayActivityOverHome)) { final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID); @@ -625,7 +622,7 @@ class ActivityStarter { FLAG_ACTIVITY_TASK_ON_HOME); ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle) : ActivityOptions.makeBasic()); - options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId); + options.setLaunchTaskId(mSupervisor.getHomeActivity().getTask().taskId); mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); } @@ -763,7 +760,7 @@ class ActivityStarter { newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName); newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, - hist.task.taskId); + hist.getTask().taskId); } newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, aInfo.packageName); @@ -967,8 +964,8 @@ class ActivityStarter { // If we are not able to proceed, disassociate the activity from the task. Leaving an // activity in an incomplete state can lead to issues, such as performing operations // without a window container. - if (result != START_SUCCESS && mStartActivity.task != null) { - mStartActivity.task.removeActivity(mStartActivity); + if (result != START_SUCCESS && mStartActivity.getTask() != null) { + mStartActivity.getTask().removeActivity(mStartActivity); } mService.mWindowManager.continueSurfaceLayout(); } @@ -1002,7 +999,7 @@ class ActivityStarter { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and // the device would otherwise leave the locked task. - if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task, + if (mSupervisor.isLockTaskModeViolation(mReusedActivity.getTask(), (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { mSupervisor.showLockTaskToast(); @@ -1010,13 +1007,13 @@ class ActivityStarter { return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - if (mStartActivity.task == null) { - mStartActivity.task = mReusedActivity.task; + if (mStartActivity.getTask() == null) { + mStartActivity.setTask(mReusedActivity.getTask()); } - if (mReusedActivity.task.intent == null) { + if (mReusedActivity.getTask().intent == null) { // This task was started because of movement of the activity based on affinity... // Now that we are actually launching it, we can assign the base intent. - mReusedActivity.task.setIntent(mStartActivity); + mReusedActivity.getTask().setIntent(mStartActivity); } // This code path leads to delivering a new intent, we want to make sure we schedule it @@ -1025,7 +1022,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || mLaunchSingleInstance || mLaunchSingleTask) { - final TaskRecord task = mReusedActivity.task; + final TaskRecord task = mReusedActivity.getTask(); // In this situation we want to remove all activities from the task up to the one // being started. In most cases this means we are resetting the task to its initial @@ -1037,17 +1034,17 @@ class ActivityStarter { // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The // task reference is needed in the call below to // {@link setTargetStackAndMoveToFrontIfNeeded}. - if (mReusedActivity.task == null) { - mReusedActivity.task = task; + if (mReusedActivity.getTask() == null) { + mReusedActivity.setTask(task); } if (top != null) { if (top.frontOfTask) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. - top.task.setIntent(mStartActivity); + top.getTask().setIntent(mStartActivity); } - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); } @@ -1098,7 +1095,7 @@ class ActivityStarter { && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) { - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { @@ -1116,14 +1113,14 @@ class ActivityStarter { // Don't use mStartActivity.task to show the toast. We're not starting a new activity // but reusing 'top'. Fields in mStartActivity may not be fully initialized. mSupervisor.handleNonResizableTaskIfNeeded( - top.task, preferredLaunchStackId, topStack.mStackId); + top.getTask(), preferredLaunchStackId, topStack.mStackId); return START_DELIVERED_TO_TOP; } boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) - ? mSourceRecord.task : null; + ? mSourceRecord.getTask() : null; // Should this be considered a new task? int result = START_SUCCESS; @@ -1150,14 +1147,15 @@ class ActivityStarter { mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent, mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid)); if (mSourceRecord != null) { - mStartActivity.task.setTaskToReturnTo(mSourceRecord); + mStartActivity.getTask().setTaskToReturnTo(mSourceRecord); } if (newTask) { EventLog.writeEvent( - EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId); + EventLogTags.AM_CREATE_TASK, mStartActivity.userId, + mStartActivity.getTask().taskId); } ActivityStack.logStartActivity( - EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task); + EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); mTargetStack.mLastPausedActivity = null; sendPowerHintForLaunchStartIfNeeded(false /* forceSend */); @@ -1165,7 +1163,8 @@ class ActivityStarter { mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); if (mDoResume) { - final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked(); + final ActivityRecord topTaskActivity = + mStartActivity.getTask().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { @@ -1197,7 +1196,7 @@ class ActivityStarter { mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded( - mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId); + mStartActivity.getTask(), preferredLaunchStackId, mTargetStack.mStackId); return START_SUCCESS; } @@ -1424,7 +1423,7 @@ class ActivityStarter { + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; mNewTaskInfo = mSourceRecord.info; - mNewTaskIntent = mSourceRecord.task.intent; + mNewTaskIntent = mSourceRecord.getTask().intent; } mSourceRecord = null; mSourceStack = null; @@ -1516,15 +1515,16 @@ class ActivityStarter { ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); - if (curTop != null - && (curTop.task != intentActivity.task || curTop.task != focusStack.topTask()) + final TaskRecord topTask = curTop != null ? curTop.getTask() : null; + if (topTask != null + && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()) && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || (mSourceStack.topActivity() != null && - mSourceStack.topActivity().task == mSourceRecord.task)) { + mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) { // We really do want to push this one into the user's face, right now. if (mLaunchTaskBehind && mSourceRecord != null) { - intentActivity.setTaskToAffiliateWith(mSourceRecord.task); + intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } mMovedOtherTask = true; @@ -1539,13 +1539,13 @@ class ActivityStarter { == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); if (!willClearTask) { final ActivityStack launchStack = getLaunchStack( - mStartActivity, mLaunchFlags, mStartActivity.task, mOptions); + mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); + final TaskRecord intentTask = intentActivity.getTask(); if (launchStack == null || launchStack == mTargetStack) { // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. - mTargetStack.moveTaskToFrontLocked( - intentActivity.task, mNoAnimation, mOptions, + mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); mMovedToFront = true; } else if (launchStack.mStackId == DOCKED_STACK_ID @@ -1553,7 +1553,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // If we want to launch adjacent and mTargetStack is not the computed // launch stack - move task to top of computed stack. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentTask.reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "launchToSide"); } else { @@ -1561,8 +1561,8 @@ class ActivityStarter { // We choose to move task to front instead of launching it adjacent // when specific stack was requested explicitly and it appeared to be // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set. - mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation, - mOptions, mStartActivity.appTimeTracker, + mTargetStack.moveTaskToFrontLocked(intentTask, + mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringToFrontInsteadOfAdjacentLaunch"); } mMovedToFront = true; @@ -1570,7 +1570,7 @@ class ActivityStarter { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and // move it to front. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToDisplay"); mMovedToFront = true; @@ -1582,7 +1582,7 @@ class ActivityStarter { intentActivity.showStartingWindow(null /* prev */, false /* newTask */, true /* taskSwitch */); } - updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack); + updateTaskReturnToType(intentActivity.getTask(), mLaunchFlags, focusStack); } } if (!mMovedToFront && mDoResume) { @@ -1591,7 +1591,7 @@ class ActivityStarter { mTargetStack.moveToFront("intentActivityFound"); } - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID, + mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID, mTargetStack.mStackId); // If the caller has requested that the target task be reset, then do so. @@ -1635,7 +1635,7 @@ class ActivityStarter { // launching another activity. // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are // already launching one. - final TaskRecord task = intentActivity.task; + final TaskRecord task = intentActivity.getTask(); task.performClearTaskLocked(); mReuseTask = task; mReuseTask.setIntent(mStartActivity); @@ -1646,7 +1646,7 @@ class ActivityStarter { mMovedOtherTask = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || mLaunchSingleInstance || mLaunchSingleTask) { - ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity, + ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity, mLaunchFlags); if (top == null) { // A special case: we need to start the activity because it is not currently @@ -1655,11 +1655,11 @@ class ActivityStarter { mAddingToTask = true; // We are no longer placing the activity in the task we previously thought we were. - mStartActivity.task = null; + mStartActivity.setTask(null); // Now pretend like this activity is being started by the top of its task, so it // is put in the right place. mSourceRecord = intentActivity; - final TaskRecord task = mSourceRecord.task; + final TaskRecord task = mSourceRecord.getTask(); if (task != null && task.getStack() == null) { // Target stack got cleared when we all activities were removed above. // Go ahead and reset it. @@ -1669,7 +1669,7 @@ class ActivityStarter { !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } } - } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) { + } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) { // In this case the top activity on the task is the same as the one being launched, // so we take that as a request to bring the task to the foreground. If the top // activity in the task is the root activity, deliver this new intent to it if it @@ -1677,13 +1677,13 @@ class ActivityStarter { if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) && intentActivity.realActivity.equals(mStartActivity.realActivity)) { ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, - intentActivity.task); + intentActivity.getTask()); if (intentActivity.frontOfTask) { - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); - } else if (!intentActivity.task.isSameIntentFilter(mStartActivity)) { + } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { // In this case we are launching the root activity of the task, but with a // different intent. We should start a new instance on top. mAddingToTask = true; @@ -1696,13 +1696,13 @@ class ActivityStarter { // current task. mAddingToTask = true; mSourceRecord = intentActivity; - } else if (!intentActivity.task.rootWasReset) { + } else if (!intentActivity.getTask().rootWasReset) { // In this case we are launching into an existing task that has not yet been started // from its front door. The current task has been brought to the front. Ideally, // we'd probably like to place this new task at the bottom of its stack, but that's // a little hard to do with the current organization of the code so for now we'll // just drop it. - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } } @@ -1736,11 +1736,11 @@ class ActivityStarter { mService.resizeStack( stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); } else { - mStartActivity.task.updateOverrideConfiguration(mLaunchBounds); + mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds); } } if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new task " + mStartActivity.task); + + " in new task " + mStartActivity.getTask()); } else { addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -1749,7 +1749,7 @@ class ActivityStarter { mStartActivity.setTaskToAffiliateWith(taskToAffiliate); } - if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) { + if (mSupervisor.isLockTaskModeViolation(mStartActivity.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } @@ -1758,7 +1758,7 @@ class ActivityStarter { // If stack id is specified in activity options, usually it means that activity is // launched not from currently focused stack (e.g. from SysUI or from shell) - in // that case we check the target stack. - updateTaskReturnToType(mStartActivity.task, mLaunchFlags, + updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags, preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack); } if (mDoResume) { @@ -1768,19 +1768,19 @@ class ActivityStarter { } private int setTaskFromSourceRecord() { - if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) { + if (mSupervisor.isLockTaskModeViolation(mSourceRecord.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - final TaskRecord sourceTask = mSourceRecord.task; + final TaskRecord sourceTask = mSourceRecord.getTask(); final ActivityStack sourceStack = mSourceRecord.getStack(); // We only want to allow changing stack if the target task is not the top one, // otherwise we would move the launching task to the other side, rather than show // two side by side. final boolean moveStackAllowed = sourceStack.topTask() != sourceTask; if (moveStackAllowed) { - mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, + mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); } @@ -1805,7 +1805,7 @@ class ActivityStarter { ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags); mKeepCurTransition = true; if (top != null) { - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; @@ -1821,7 +1821,7 @@ class ActivityStarter { // stack if so. final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity); if (top != null) { - final TaskRecord task = top.task; + final TaskRecord task = top.getTask(); task.moveActivityToFrontLocked(top); top.updateOptionsLocked(mOptions); ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task); @@ -1838,7 +1838,7 @@ class ActivityStarter { // the same task as the one that is starting it. addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in existing task " + mStartActivity.task + " from source " + mSourceRecord); + + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord); return START_SUCCESS; } @@ -1861,7 +1861,7 @@ class ActivityStarter { || mLaunchSingleTop || mLaunchSingleTask) { mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront"); - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { // We don't need to start a new activity, and the client said not to do // anything if that is the case, so this is it! @@ -1901,7 +1901,7 @@ class ActivityStarter { addOrReparentStartingActivity(mInTask, "setTaskFromInTask"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in explicit task " + mStartActivity.task); + + " in explicit task " + mStartActivity.getTask()); return START_SUCCESS; } @@ -1913,17 +1913,17 @@ class ActivityStarter { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.topActivity(); - final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord( + final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info, mIntent, null, null, true, mStartActivity.mActivityType); addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new guessed " + mStartActivity.task); + + " in new guessed " + mStartActivity.getTask()); } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { - if (mStartActivity.task == null || mStartActivity.task == parent) { + if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addActivityToTop(mStartActivity); } else { mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason); @@ -1973,7 +1973,7 @@ class ActivityStarter { private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds, int launchFlags, ActivityOptions aOptions) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions); if (stack != null) { return stack; diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 21c131c3616f..ba72dcf23c98 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -98,6 +98,7 @@ class AppErrors { AppErrors(Context context, ActivityManagerService service) { + context.assertRuntimeOverlayThemable(); mService = service; mContext = context; } diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java index 5f7f67a8a5e8..347a357aae04 100644 --- a/services/core/java/com/android/server/am/BaseErrorDialog.java +++ b/services/core/java/com/android/server/am/BaseErrorDialog.java @@ -34,6 +34,7 @@ class BaseErrorDialog extends AlertDialog { public BaseErrorDialog(Context context) { super(context, com.android.internal.R.style.Theme_Dialog_AppError); + context.assertRuntimeOverlayThemable(); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java new file mode 100644 index 000000000000..c34c097cb696 --- /dev/null +++ b/services/core/java/com/android/server/am/PersistentConnection.java @@ -0,0 +1,168 @@ +/* + * 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.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; + +/** + * Connects to a given service component on a given user. + * + * - Call {@link #connect()} to create a connection. + * - Call {@link #disconnect()} to disconnect. Make sure to disconnect when the user stops. + * + * Add onConnected/onDisconnected callbacks as needed. + */ +public abstract class PersistentConnection<T> { + private final Object mLock = new Object(); + + private final String mTag; + private final Context mContext; + private final Handler mHandler; + private final int mUserId; + private final ComponentName mComponentName; + + @GuardedBy("mLock") + private boolean mStarted; + + @GuardedBy("mLock") + private boolean mIsConnected; + + @GuardedBy("mLock") + private T mService; + + private final ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mLock) { + Slog.i(mTag, "Connected: " + mComponentName.flattenToShortString() + + " u" + mUserId); + + mIsConnected = true; + mService = asInterface(service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (mLock) { + Slog.i(mTag, "Disconnected: " + mComponentName.flattenToShortString() + + " u" + mUserId); + + cleanUpConnectionLocked(); + } + } + }; + + public PersistentConnection(@NonNull String tag, @NonNull Context context, + @NonNull Handler handler, int userId, @NonNull ComponentName componentName) { + mTag = tag; + mContext = context; + mHandler = handler; + mUserId = userId; + mComponentName = componentName; + } + + public final ComponentName getComponentName() { + return mComponentName; + } + + /** + * @return whether connected. + */ + public final boolean isConnected() { + synchronized (mLock) { + return mIsConnected; + } + } + + /** + * @return the service binder interface. + */ + public final T getServiceBinder() { + synchronized (mLock) { + return mService; + } + } + + /** + * Connects to the service. + */ + public final void connect() { + synchronized (mLock) { + if (mStarted) { + return; + } + mStarted = true; + + final Intent service = new Intent().setComponent(mComponentName); + + final boolean success = mContext.bindServiceAsUser(service, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + mHandler, UserHandle.of(mUserId)); + + if (!success) { + Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId + + " failed."); + } + } + } + + private void cleanUpConnectionLocked() { + mIsConnected = false; + mService = null; + } + + /** + * Disconnect from the service. + */ + public final void disconnect() { + synchronized (mLock) { + if (!mStarted) { + return; + } + Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId); + mStarted = false; + mContext.unbindService(mServiceConnection); + + cleanUpConnectionLocked(); + } + } + + /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */ + protected abstract T asInterface(IBinder binder); + + public void dump(String prefix, PrintWriter pw) { + synchronized (mLock) { + pw.print(prefix); + pw.print(mComponentName.flattenToShortString()); + pw.print(mStarted ? " [started]" : " [not started]"); + pw.print(mIsConnected ? " [connected]" : " [not connected]"); + pw.println(); + } + } +} diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java index 32d3082357a2..394e902ec27e 100644 --- a/services/core/java/com/android/server/am/PinnedActivityStack.java +++ b/services/core/java/com/android/server/am/PinnedActivityStack.java @@ -23,6 +23,7 @@ import com.android.server.am.ActivityStackSupervisor.ActivityContainer; import com.android.server.wm.PinnedStackWindowController; import com.android.server.wm.StackWindowController; +import java.util.ArrayList; import java.util.List; /** @@ -57,4 +58,18 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> { boolean isBoundsAnimatingToFullscreen() { return getWindowContainerController().isBoundsAnimatingToFullscreen(); } + + @Override + 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 + // the animation out of PiP) + synchronized(this) { + ArrayList<TaskRecord> tasks = getAllTasks(); + for (int i = 0; i < tasks.size(); i++ ) { + mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i), + targetStackBounds, true /* immediate */); + } + } + } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 40effff77166..0dc678813338 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -350,58 +350,58 @@ final class ProcessList { String procState; switch (curProcState) { case ActivityManager.PROCESS_STATE_PERSISTENT: - procState = "P "; + procState = "PER "; break; case ActivityManager.PROCESS_STATE_PERSISTENT_UI: - procState = "PU"; + procState = "PERU"; break; case ActivityManager.PROCESS_STATE_TOP: - procState = "T "; + procState = "TOP"; break; case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - procState = "SB"; + procState = "BFGS"; break; case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: - procState = "SF"; + procState = "FGS "; break; case ActivityManager.PROCESS_STATE_TOP_SLEEPING: - procState = "TS"; + procState = "TPSL"; break; case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: - procState = "IF"; + procState = "IMPF"; break; case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: - procState = "IB"; + procState = "IMPB"; break; case ActivityManager.PROCESS_STATE_BACKUP: - procState = "BU"; + procState = "BKUP"; break; case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: - procState = "HW"; + procState = "HVY "; break; case ActivityManager.PROCESS_STATE_SERVICE: - procState = "S "; + procState = "SVC "; break; case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "R "; + procState = "RCVR"; break; case ActivityManager.PROCESS_STATE_HOME: - procState = "HO"; + procState = "HOME"; break; case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: - procState = "LA"; + procState = "LAST"; break; case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: - procState = "CA"; + procState = "CAC "; break; case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - procState = "Ca"; + procState = "CACC"; break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: - procState = "CE"; + procState = "CEM "; break; case ActivityManager.PROCESS_STATE_NONEXISTENT: - procState = "N "; + procState = "NONE"; break; default: procState = "??"; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 2d2720458fbb..3c5c5fd0cfe4 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -287,7 +287,9 @@ final class ProcessRecord { pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); - pw.print(prefix); pw.print("vrThreadTid="); pw.print(vrThreadTid); + if (vrThreadTid != 0) { + pw.print(prefix); pw.print("vrThreadTid="); pw.println(vrThreadTid); + } pw.print(prefix); pw.print("curProcState="); pw.print(curProcState); pw.print(" repProcState="); pw.print(repProcState); pw.print(" pssProcState="); pw.print(pssProcState); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 44ebf50eb31a..b57f6c366f8f 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -124,6 +124,7 @@ final class ServiceRecord extends Binder { final ServiceRecord sr; final boolean taskRemoved; final int id; + final int callingId; final Intent intent; final ActivityManagerService.NeededUriGrants neededGrants; long deliveredTime; @@ -134,12 +135,13 @@ final class ServiceRecord extends Binder { String stringName; // caching of toString StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, - ActivityManagerService.NeededUriGrants _neededGrants) { + ActivityManagerService.NeededUriGrants _neededGrants, int _callingId) { sr = _sr; taskRemoved = _taskRemoved; id = _id; intent = _intent; neededGrants = _neededGrants; + callingId = _callingId; } UriPermissionOwner getUriPermissionsLocked() { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index ce32f8413468..c7f20b9ff904 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -568,7 +568,27 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, String reason) { return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate, - deferResume, reason); + deferResume, true /* schedulePictureInPictureModeChange */, reason); + } + + /** + * Convenience method to reparent a task to the top or bottom position of the stack, with + * an option to skip scheduling the picture-in-picture mode change. + */ + boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode, + boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, + String reason) { + return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate, + deferResume, schedulePictureInPictureModeChange, reason); + } + + /** + * Convenience method to reparent a task to a specific position of the stack. + */ + boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode, + boolean animate, boolean deferResume, String reason) { + return reparent(preferredStackId, position, moveStackMode, animate, deferResume, + true /* schedulePictureInPictureModeChange */, reason); } /** @@ -577,16 +597,20 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta * @param preferredStackId the stack id of the target stack to move this task * @param position the position to place this task in the new stack * @param animate whether or not we should wait for the new window created as a part of the - * reparenting to be drawn and animated in + * reparenting to be drawn and animated in * @param moveStackMode whether or not to move the stack to the front always, only if it was - * previously focused & in front, or never + * previously focused & in front, or never * @param deferResume whether or not to update the visibility of other tasks and stacks that may - * have changed as a result of this reparenting + * have changed as a result of this reparenting + * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode + * change. Callers may set this to false if they are explicitly scheduling PiP mode + * changes themselves, like during the PiP animation * @param reason the caller of this reparenting - * @return + * @return whether the task was reparented */ boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode, - boolean animate, boolean deferResume, String reason) { + boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, + String reason) { final ActivityStackSupervisor supervisor = mService.mStackSupervisor; final WindowManagerService windowManager = mService.mWindowManager; final ActivityStack sourceStack = getStack(); @@ -636,24 +660,14 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta // we are coming from in WM before we reparent because it became empty. mWindowContainerController.reparent(toStack.getWindowContainerController(), position); - // Reset the resumed activity on the previous stack - if (wasResumed) { - sourceStack.mResumedActivity = null; - } - - // Reset the paused activity on the previous stack - if (wasPaused) { - sourceStack.mPausingActivity = null; - sourceStack.removeTimeoutsForActivityLocked(r); - } - // Move the task sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING); - toStack.addTask(this, position, reason); + toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason); - // TODO: Ensure that this is actually necessary here - // Notify of picture-in-picture mode changes - supervisor.scheduleReportPictureInPictureModeChangedIfNeeded(this, sourceStack); + if (schedulePictureInPictureModeChange) { + // Notify of picture-in-picture mode changes + supervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, sourceStack); + } // TODO: Ensure that this is actually necessary here // Notify the voice session if required @@ -1187,14 +1201,13 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta * be in the current task or unparented to any task. */ void addActivityAtIndex(int index, ActivityRecord r) { - if (r.task != null && r.task != this) { + TaskRecord task = r.getTask(); + if (task != null && task != this) { throw new IllegalArgumentException("Can not add r=" + " to task=" + this - + " current parent=" + r.task); + + " current parent=" + task); } - // TODO(b/36505427): Maybe make task private to ActivityRecord so we can also do - // onParentChanged() within the setter? - r.task = this; - r.onParentChanged(); + + r.setTask(this); // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. if (!mActivities.remove(r) && r.fullscreen) { @@ -1249,15 +1262,21 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta } /** - * @return true if this was the last activity in the task + * Removes the specified activity from this task. + * @param r The {@link ActivityRecord} to remove. + * @return true if this was the last activity in the task. */ boolean removeActivity(ActivityRecord r) { - if (r.task != this) { + return removeActivity(r, false /*reparenting*/); + } + + boolean removeActivity(ActivityRecord r, boolean reparenting) { + if (r.getTask() != this) { throw new IllegalArgumentException( "Activity=" + r + " does not belong to task=" + this); } - r.task = null; + r.setTask(null /*task*/, reparenting); if (mActivities.remove(r) && r.fullscreen) { // Was previously in list. @@ -1412,7 +1431,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta TaskThumbnail getTaskThumbnailLocked() { if (mStack != null) { final ActivityRecord resumedActivity = mStack.mResumedActivity; - if (resumedActivity != null && resumedActivity.task == this) { + if (resumedActivity != null && resumedActivity.getTask() == this) { final Bitmap thumbnail = resumedActivity.screenshotActivityLocked(); setLastThumbnailLocked(thumbnail); } @@ -1594,6 +1613,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta String iconFilename = null; int colorPrimary = 0; int colorBackground = 0; + int statusBarColor = 0; + int navigationBarColor = 0; + boolean topActivity = true; for (--activityNdx; activityNdx >= 0; --activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); if (r.taskDescription != null) { @@ -1606,13 +1628,16 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta if (colorPrimary == 0) { colorPrimary = r.taskDescription.getPrimaryColor(); } - if (colorBackground == 0) { + if (topActivity) { colorBackground = r.taskDescription.getBackgroundColor(); + statusBarColor = r.taskDescription.getStatusBarColor(); + navigationBarColor = r.taskDescription.getNavigationBarColor(); } } + topActivity = false; } lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary, - colorBackground); + colorBackground, statusBarColor, navigationBarColor); if (mWindowContainerController != null) { mWindowContainerController.setTaskDescription(lastTaskDescription); } @@ -1928,7 +1953,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { - activities.get(activityNdx).task = task; + activities.get(activityNdx).setTask(task); } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task); @@ -1979,6 +2004,25 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta } /** + * @return a new Configuration for this Task, given the provided {@param bounds} and + * {@param insetBounds}. + */ + Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) { + // Compute a new override configuration for the given bounds, if fullscreen bounds + // (bounds == null), then leave the override config unset + final Configuration newOverrideConfig = new Configuration(); + if (bounds != null) { + newOverrideConfig.setTo(getOverrideConfiguration()); + mTmpRect.set(bounds); + adjustForMinimalTaskDimensions(mTmpRect); + computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds, + mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom); + } + + return newOverrideConfig; + } + + /** * Update task's override configuration based on the bounds. * @param bounds The bounds of the task. * @return True if the override configuration was updated. @@ -2027,7 +2071,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta onOverrideConfigurationChanged(newConfig); if (mFullscreen != oldFullscreen) { - mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this); + mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this); } return !mTmpConfig.equals(newConfig); @@ -2044,6 +2088,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta config.unset(); final Configuration parentConfig = getParent().getConfiguration(); + final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; if (mStack != null) { @@ -2052,11 +2097,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density, config, parentConfig); } else { - // No stack, give some default values - config.smallestScreenWidthDp = - mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask; - config.screenWidthDp = config.screenHeightDp = config.smallestScreenWidthDp; - Slog.wtf(TAG, "Expected stack when calculating override config"); + throw new IllegalArgumentException("Expected stack when calculating override config"); } config.orientation = (config.screenWidthDp <= config.screenHeightDp) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 70e56b09de13..c11f5316f769 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -55,6 +55,7 @@ import android.hardware.hdmi.HdmiTvClient; import android.hardware.usb.UsbManager; import android.media.AudioAttributes; import android.media.AudioDevicePort; +import android.media.AudioFocusInfo; import android.media.AudioSystem; import android.media.AudioFormat; import android.media.AudioManager; @@ -5634,8 +5635,9 @@ public class AudioService extends IAudioService.Stub clientId, callingPackageName, flags); } - public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) { - return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa); + public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, + String callingPackageName) { + return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } public void unregisterAudioFocusClient(String clientId) { @@ -5650,6 +5652,7 @@ public class AudioService extends IAudioService.Stub return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr); } + //========================================================================================== private boolean readCameraSoundForced() { return SystemProperties.getBoolean("audio.camerasound.force", false) || mContext.getResources().getBoolean( @@ -6430,7 +6433,7 @@ public class AudioService extends IAudioService.Stub // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, - boolean hasFocusListener) { + boolean hasFocusListener, boolean isFocusPolicy) { AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback); if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() @@ -6452,7 +6455,8 @@ public class AudioService extends IAudioService.Stub Slog.e(TAG, "Cannot re-register policy"); return null; } - AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener); + AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener, + isFocusPolicy); pcb.asBinder().linkToDeath(app, 0/*flags*/); regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); @@ -6650,15 +6654,21 @@ public class AudioService extends IAudioService.Stub * is handling ducking for audio focus. */ int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT; + boolean mIsFocusPolicy = false; AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, - boolean hasFocusListener) { + boolean hasFocusListener, boolean isFocusPolicy) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mPolicyCallback = token; mHasFocusListener = hasFocusListener; if (mHasFocusListener) { mMediaFocusControl.addFocusFollower(mPolicyCallback); + // can only ever be true if there is a focus listener + if (isFocusPolicy) { + mIsFocusPolicy = true; + mMediaFocusControl.setFocusPolicy(mPolicyCallback); + } } connectMixes(); } @@ -6676,6 +6686,9 @@ public class AudioService extends IAudioService.Stub } void release() { + if (mIsFocusPolicy) { + mMediaFocusControl.unsetFocusPolicy(mPolicyCallback); + } if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { mMediaFocusControl.setDuckingInExtPolicyAvailable(false); } @@ -6690,6 +6703,22 @@ public class AudioService extends IAudioService.Stub } }; + //====================== + // Audio policy: focus + //====================== + /** */ + public int dispatchFocusChange(AudioFocusInfo afi, int focusChange, IAudioPolicyCallback pcb) { + synchronized (mAudioPolicies) { + if (!mAudioPolicies.containsKey(pcb.asBinder())) { + throw new IllegalStateException("Unregistered AudioPolicy for focus dispatch"); + } + return mMediaFocusControl.dispatchFocusChange(afi, focusChange); + } + } + + //====================== + // misc + //====================== private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies = new HashMap<IBinder, AudioPolicyProxy>(); private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java index 5275c0524d54..bcaa29593304 100644 --- a/services/core/java/com/android/server/audio/FocusRequester.java +++ b/services/core/java/com/android/server/audio/FocusRequester.java @@ -33,7 +33,7 @@ import java.io.PrintWriter; * @hide * Class to handle all the information about a user of audio focus. The lifecycle of each * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus - * stack to its release. + * stack, or the map of focus owners for an external focus policy, to its release. */ public class FocusRequester { @@ -101,6 +101,21 @@ public class FocusRequester { mFocusController = ctlr; } + FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, + IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) { + mAttributes = afi.getAttributes(); + mClientId = afi.getClientId(); + mPackageName = afi.getPackageName(); + mCallingUid = afi.getClientUid(); + mFocusGainRequest = afi.getGainRequest(); + mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; + mGrantFlags = afi.getFlags(); + + mFocusDispatcher = afl; + mSourceRef = source; + mDeathHandler = hdlr; + mFocusController = ctlr; + } boolean hasSameClient(String otherClient) { try { @@ -118,6 +133,10 @@ public class FocusRequester { return (mSourceRef != null) && mSourceRef.equals(ib); } + boolean hasSameDispatcher(IAudioFocusDispatcher fd) { + return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd); + } + boolean hasSamePackage(String pack) { try { return mPackageName.compareTo(pack) == 0; @@ -369,6 +388,35 @@ public class FocusRequester { } } + int dispatchFocusChange(int focusChange) { + if (mFocusDispatcher == null) { + if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: no focus dispatcher"); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + if (focusChange == AudioManager.AUDIOFOCUS_NONE) { + if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE + || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT + || focusChange == AudioManager.AUDIOFOCUS_GAIN) + && (mFocusGainRequest != focusChange)){ + Log.w(TAG, "focus gain was requested with " + mFocusGainRequest + + ", dispatching " + focusChange); + } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK + || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT + || focusChange == AudioManager.AUDIOFOCUS_LOSS) { + mFocusLossReceived = focusChange; + } + try { + mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId); + } catch (android.os.RemoteException e) { + Log.v(TAG, "dispatchFocusChange: error talking to focus listener", e); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; + } + AudioFocusInfo toAudioFocusInfo() { return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName, mFocusGainRequest, mFocusLossReceived, mGrantFlags); diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index b3f1548d4d92..821e78a27e33 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -16,6 +16,7 @@ package com.android.server.audio; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.Context; import android.media.AudioAttributes; @@ -23,6 +24,7 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; +import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.IBinder; @@ -32,7 +34,10 @@ import android.util.Log; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; import java.util.Stack; import java.text.DateFormat; @@ -43,6 +48,7 @@ import java.text.DateFormat; public class MediaFocusControl implements PlayerFocusEnforcer { private static final String TAG = "MediaFocusControl"; + static final boolean DEBUG = false; /** * set to true so the framework enforces ducking itself, without communicating to apps @@ -155,6 +161,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer { while(stackIterator.hasNext()) { stackIterator.next().dump(pw); } + pw.println("\n"); + if (mFocusPolicy == null) { + pw.println("No external focus policy\n"); + } else { + pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n"); + dumpExtFocusPolicyFocusOwners(pw); + } } pw.println("\n"); pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n"); @@ -234,6 +247,31 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } /** + * Helper function for external focus policy: + * Called synchronized on mAudioFocusLock + * Remove focus listeners from the list of potential focus owners for a particular client when + * it has died. + */ + private void removeFocusEntryForExtPolicy(IBinder cb) { + if (mFocusOwnersForFocusPolicy.isEmpty()) { + return; + } + boolean released = false; + final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); + final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); + while (ownerIterator.hasNext()) { + final Entry<String, FocusRequester> owner = ownerIterator.next(); + final FocusRequester fr = owner.getValue(); + if (fr.hasSameBinder(cb)) { + ownerIterator.remove(); + fr.release(); + notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo()); + break; + } + } + } + + /** * Helper function: * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. * The implementation guarantees that a state where focus cannot be immediately reassigned @@ -297,7 +335,11 @@ public class MediaFocusControl implements PlayerFocusEnforcer { public void binderDied() { synchronized(mAudioFocusLock) { - removeFocusStackEntryOnDeath(mCb); + if (mFocusPolicy != null) { + removeFocusEntryForExtPolicy(mCb); + } else { + removeFocusStackEntryOnDeath(mCb); + } } } } @@ -353,6 +395,34 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + private IAudioPolicyCallback mFocusPolicy = null; + + // Since we don't have a stack of focus owners when using an external focus policy, we keep + // track of all the focus requesters in this map, with their clientId as the key. This is + // used both for focus dispatch and death handling + private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy = + new HashMap<String, FocusRequester>(); + + void setFocusPolicy(IAudioPolicyCallback policy) { + if (policy == null) { + return; + } + synchronized (mAudioFocusLock) { + mFocusPolicy = policy; + } + } + + void unsetFocusPolicy(IAudioPolicyCallback policy) { + if (policy == null) { + return; + } + synchronized (mAudioFocusLock) { + if (mFocusPolicy == policy) { + mFocusPolicy = null; + } + } + } + /** * @param pcb non null */ @@ -409,6 +479,100 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + /** + * Called synchronized on mAudioFocusLock + * @param afi + * @param requestResult + * @return true if the external audio focus policy (if any) is handling the focus request + */ + boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult, + IAudioFocusDispatcher fd, IBinder cb) { + if (mFocusPolicy == null) { + return false; + } + if (DEBUG) { + Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId() + + " dispatcher=" + fd); + } + final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); + if (existingFr != null) { + if (!existingFr.hasSameDispatcher(fd)) { + existingFr.release(); + final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); + mFocusOwnersForFocusPolicy.put(afi.getClientId(), + new FocusRequester(afi, fd, cb, hdlr, this)); + } + } else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED + || requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { + // new focus (future) focus owner to keep track of + final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); + mFocusOwnersForFocusPolicy.put(afi.getClientId(), + new FocusRequester(afi, fd, cb, hdlr, this)); + } + try { + //oneway + mFocusPolicy.notifyAudioFocusRequest(afi, requestResult); + } catch (RemoteException e) { + Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback " + + mFocusPolicy.asBinder(), e); + } + return true; + } + + /** + * Called synchronized on mAudioFocusLock + * @param afi + * @param requestResult + * @return true if the external audio focus policy (if any) is handling the focus request + */ + boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) { + if (mFocusPolicy == null) { + return false; + } + final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); + if (fr != null) { + fr.release(); + } + try { + //oneway + mFocusPolicy.notifyAudioFocusAbandon(afi); + } catch (RemoteException e) { + Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback " + + mFocusPolicy.asBinder(), e); + } + return true; + } + + /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */ + int dispatchFocusChange(AudioFocusInfo afi, int focusChange) { + if (DEBUG) { + Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client=" + + afi.getClientId()); + } + synchronized (mAudioFocusLock) { + if (mFocusPolicy == null) { + if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); + if (fr == null) { + if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return fr.dispatchFocusChange(focusChange); + } + } + + private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) { + final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); + final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); + while (ownerIterator.hasNext()) { + final Entry<String, FocusRequester> owner = ownerIterator.next(); + final FocusRequester fr = owner.getValue(); + fr.dump(pw); + } + } + protected int getCurrentAudioFocus() { synchronized(mAudioFocusLock) { if (mFocusStack.empty()) { @@ -487,10 +651,23 @@ public class MediaFocusControl implements PlayerFocusEnforcer { & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (enteringRingOrCall) { mRingOrCallActive = true; } + final AudioFocusInfo afiForExtPolicy; + if (mFocusPolicy != null) { + // construct AudioFocusInfo as it will be communicated to audio focus policy + afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(), + clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, + flags); + } else { + afiForExtPolicy = null; + } + + // handle delayed focus boolean focusGrantDelayed = false; if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { - return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED; + notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb); + return result; } else { // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be // granted right now, so the requester will be inserted in the focus stack @@ -499,6 +676,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + // external focus policy: delay request for focus gain? + final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED; + if (notifyExtFocusPolicyFocusRequest_syncAf( + afiForExtPolicy, resultWithExtPolicy, fd, cb)) { + // stop handling focus request here as it is handled by external audio focus policy + return resultWithExtPolicy; + } + // handle the potential premature death of the new holder of the focus // (premature death == death before abandoning focus) // Register for client death notification @@ -569,7 +754,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer { /** * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) * */ - protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) { + protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, + String callingPackageName) { // AudioAttributes are currently ignored, to be used for zones Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() @@ -577,6 +763,16 @@ public class MediaFocusControl implements PlayerFocusEnforcer { try { // this will take care of notifying the new focus owner if needed synchronized(mAudioFocusLock) { + // external focus policy? + if (mFocusPolicy != null) { + final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(), + clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/, + 0 /*flags*/); + if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; + } + } + boolean exitingRingOrCall = mRingOrCallActive & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (exitingRingOrCall) { mRingOrCallActive = false; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a1a74377cf62..ddd918fdcc5d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -339,6 +339,18 @@ public final class DisplayManagerService extends SystemService { } } + /** + * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) + */ + private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) { + synchronized (mSyncRoot) { + final LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + display.getNonOverrideDisplayInfoLocked(outInfo); + } + } + } + private void performTraversalInTransactionFromWindowManagerInternal() { synchronized (mSyncRoot) { if (!mPendingTraversal) { @@ -1663,6 +1675,11 @@ public final class DisplayManagerService extends SystemService { } @Override + public void getNonOverrideDisplayInfo(int displayId, DisplayInfo outInfo) { + getNonOverrideDisplayInfoInternal(displayId, outInfo); + } + + @Override public void performTraversalInTransactionFromWindowManager() { performTraversalInTransactionFromWindowManagerInternal(); } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index a947b4106794..addad0b413a3 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -17,6 +17,7 @@ package com.android.server.display; import android.graphics.Rect; +import android.hardware.display.DisplayManagerInternal; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; @@ -62,7 +63,18 @@ final class LogicalDisplay { private final int mDisplayId; private final int mLayerStack; - private DisplayInfo mOverrideDisplayInfo; // set by the window manager + /** + * Override information set by the window manager. Will be reported instead of {@link #mInfo} + * if not null. + * @see #setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo) + * @see #getDisplayInfoLocked() + */ + private DisplayInfo mOverrideDisplayInfo; + /** + * Current display info. Initialized with {@link #mBaseDisplayInfo}. Set to {@code null} if + * needs to be updated. + * @see #getDisplayInfoLocked() + */ private DisplayInfo mInfo; // The display device that this logical display is based on and which @@ -142,6 +154,13 @@ final class LogicalDisplay { } /** + * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) + */ + void getNonOverrideDisplayInfoLocked(DisplayInfo outInfo) { + outInfo.copyFrom(mBaseDisplayInfo); + } + + /** * Sets overridden logical display information from the window manager. * This method can be used to adjust application insets, rotation, and other * properties that the window manager takes care of. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 9e4432d25c0b..dc2ebb4451b2 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -27,4 +27,15 @@ public abstract class NetworkPolicyManagerInternal { * Resets all policies associated with a given user. */ public abstract void resetUserState(int userId); + + /** + * @return true if the given uid is restricted from doing networking on metered networks. + */ + public abstract boolean isUidRestrictedOnMeteredNetworks(int uid); + + /** + * @return true if networking is blocked on the given interface for the given uid according + * to current networking policies. + */ + public abstract boolean isUidNetworkingBlocked(int uid, String ifname); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 8227753cc802..02e106e92aeb 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -178,6 +178,7 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; @@ -213,6 +214,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Calendar; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -246,8 +248,8 @@ import java.util.concurrent.TimeUnit; */ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { static final String TAG = "NetworkPolicy"; - private static final boolean LOGD = false; - private static final boolean LOGV = false; + private static final boolean LOGD = true; // UNDO + private static final boolean LOGV = true; // UNDO private static final int VERSION_INIT = 1; private static final int VERSION_ADDED_SNOOZE = 2; @@ -269,11 +271,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ActivityManager.isLowRamDeviceStatic() ? 50 : 200; @VisibleForTesting - public static final int TYPE_WARNING = 0x1; + public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING; @VisibleForTesting - public static final int TYPE_LIMIT = 0x2; + public static final int TYPE_LIMIT = SystemMessage.NOTE_NET_LIMIT; @VisibleForTesting - public static final int TYPE_LIMIT_SNOOZED = 0x3; + public static final int TYPE_LIMIT_SNOOZED = SystemMessage.NOTE_NET_LIMIT_SNOOZED; private static final String TAG_POLICY_LIST = "policy-list"; private static final String TAG_NETWORK_POLICY = "network-policy"; @@ -420,15 +422,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Set of currently active {@link Notification} tags. */ @GuardedBy("mNetworkPoliciesSecondLock") - private final ArraySet<String> mActiveNotifs = new ArraySet<String>(); + private final ArraySet<NotificationId> mActiveNotifs = new ArraySet<>(); /** Foreground at UID granularity. */ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidState = new SparseIntArray(); - /** Higher priority listener before general event dispatch */ - private INetworkPolicyListener mConnectivityListener; - private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<>(); @@ -1055,7 +1054,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "updateNotificationsNL()"); // keep track of previously active notifications - final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs); + final ArraySet<NotificationId> beforeNotifs = new ArraySet<NotificationId>(mActiveNotifs); mActiveNotifs.clear(); // TODO: when switching to kernel notifications, compute next future @@ -1092,9 +1091,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // cancel stale notifications that we didn't renew above for (int i = beforeNotifs.size()-1; i >= 0; i--) { - final String tag = beforeNotifs.valueAt(i); - if (!mActiveNotifs.contains(tag)) { - cancelNotification(tag); + final NotificationId notificationId = beforeNotifs.valueAt(i); + if (!mActiveNotifs.contains(notificationId)) { + cancelNotification(notificationId); } } } @@ -1142,19 +1141,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Build unique tag that identifies an active {@link NetworkPolicy} - * notification of a specific type, like {@link #TYPE_LIMIT}. - */ - private String buildNotificationTag(NetworkPolicy policy, int type) { - return TAG + ":" + policy.template.hashCode() + ":" + type; - } - - /** * Show notification for combined {@link NetworkPolicy} and specific type, * like {@link #TYPE_LIMIT}. Okay to call multiple times. */ private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) { - final String tag = buildNotificationTag(policy, type); + final NotificationId notificationId = new NotificationId(policy, type); final Notification.Builder builder = new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS); builder.setOnlyAlertOnce(true); @@ -1262,25 +1253,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { final String packageName = mContext.getPackageName(); final int[] idReceived = new int[1]; - if(!TextUtils.isEmpty(body)) { + if (!TextUtils.isEmpty(body)) { builder.setStyle(new Notification.BigTextStyle() .bigText(body)); } mNotifManager.enqueueNotificationWithTag( - packageName, packageName, tag, 0x0, builder.build(), idReceived, - UserHandle.USER_ALL); - mActiveNotifs.add(tag); + packageName, packageName, notificationId.getTag(), notificationId.getId(), + builder.build(), idReceived, UserHandle.USER_ALL); + mActiveNotifs.add(notificationId); } catch (RemoteException e) { // ignored; service lives in system_server } } - private void cancelNotification(String tag) { + private void cancelNotification(NotificationId notificationId) { // TODO: move to NotificationManager once we can mock it try { final String packageName = mContext.getPackageName(); mNotifManager.cancelNotificationWithTag( - packageName, tag, 0x0, UserHandle.USER_ALL); + packageName, notificationId.getTag(), notificationId.getId(), + UserHandle.USER_ALL); } catch (RemoteException e) { // ignored; service lives in system_server } @@ -2242,15 +2234,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void setConnectivityListener(INetworkPolicyListener listener) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - if (mConnectivityListener != null) { - throw new IllegalStateException("Connectivity listener already registered"); - } - mConnectivityListener = listener; - } - - @Override public void registerListener(INetworkPolicyListener listener) { // TODO: create permission for observing network policy mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -3561,7 +3544,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case MSG_RULES_CHANGED: { final int uid = msg.arg1; final int uidRules = msg.arg2; - dispatchUidRulesChanged(mConnectivityListener, uid, uidRules); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3572,7 +3554,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_METERED_IFACES_CHANGED: { final String[] meteredIfaces = (String[]) msg.obj; - dispatchMeteredIfacesChanged(mConnectivityListener, meteredIfaces); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3603,7 +3584,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_RESTRICT_BACKGROUND_CHANGED: { final boolean restrictBackground = msg.arg1 != 0; - dispatchRestrictBackgroundChanged(mConnectivityListener, restrictBackground); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3621,7 +3601,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int policy = msg.arg2; final Boolean notifyApp = (Boolean) msg.obj; // First notify internal listeners... - dispatchUidPoliciesChanged(mConnectivityListener, uid, policy); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -4054,6 +4033,74 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + + /** + * @return true if the given uid is restricted from doing networking on metered networks. + */ + @Override + public boolean isUidRestrictedOnMeteredNetworks(int uid) { + final int uidRules; + final boolean isBackgroundRestricted; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); + isBackgroundRestricted = mRestrictBackground; + } + return isBackgroundRestricted + && !hasRule(uidRules, RULE_ALLOW_METERED) + && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); + } + + /** + * @return true if networking is blocked on the given interface for the given uid according + * to current networking policies. + */ + @Override + public boolean isUidNetworkingBlocked(int uid, String ifname) { + final int uidRules; + final boolean isBackgroundRestricted; + final boolean isNetworkMetered; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_NONE); + isBackgroundRestricted = mRestrictBackground; + synchronized (mNetworkPoliciesSecondLock) { + isNetworkMetered = mMeteredIfaces.contains(ifname); + } + } + if (hasRule(uidRules, RULE_REJECT_ALL)) { + if (LOGV) logUidStatus(uid, "blocked by power restrictions"); + return true; + } + if (!isNetworkMetered) { + if (LOGV) logUidStatus(uid, "allowed on unmetered network"); + return false; + } + if (hasRule(uidRules, RULE_REJECT_METERED)) { + if (LOGV) logUidStatus(uid, "blacklisted on metered network"); + return true; + } + if (hasRule(uidRules, RULE_ALLOW_METERED)) { + if (LOGV) logUidStatus(uid, "whitelisted on metered network"); + return false; + } + if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { + if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network"); + return false; + } + if (isBackgroundRestricted) { + if (LOGV) logUidStatus(uid, "blocked when background is restricted"); + return true; + } + if (LOGV) logUidStatus(uid, "allowed by default"); + return false; + } + } + + private static boolean hasRule(int uidRules, int rule) { + return (uidRules & rule) != 0; + } + + private static void logUidStatus(int uid, String descr) { + Slog.d(TAG, String.format("uid %d is %s", uid, descr)); } /** @@ -4125,4 +4172,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return next; } } + + private class NotificationId { + private final String mTag; + private final int mId; + + NotificationId(NetworkPolicy policy, int type) { + mTag = buildNotificationTag(policy, type); + mId = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NotificationId)) return false; + NotificationId that = (NotificationId) o; + return Objects.equals(mTag, that.mTag); + } + + @Override + public int hashCode() { + return Objects.hash(mTag); + } + + /** + * Build unique tag that identifies an active {@link NetworkPolicy} + * notification of a specific type, like {@link #TYPE_LIMIT}. + */ + private String buildNotificationTag(NetworkPolicy policy, int type) { + return TAG + ":" + policy.template.hashCode() + ":" + type; + } + + public String getTag() { + return mTag; + } + + public int getId() { + return mId; + } + } } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 73afaa089dd4..65aaee0f8ce8 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -282,7 +282,7 @@ public class RankingHelper implements RankingConfig { for (int i = 0; i < size; i++) { final NotificationChannel notificationChannel = r.channels.valueAt(i); if (notificationChannel != null && - notificationChannel.getId() != NotificationChannel.DEFAULT_CHANNEL_ID) { + !notificationChannel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { hasCreatedAChannel = true; break; } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index e1426fdcf3ef..f79f6f40294c 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -242,18 +242,8 @@ public class LauncherAppsService extends SystemService { try { UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); if (callingUserInfo.isManagedProfile()) { - - // STOPSHIP Remove the whitelist. - if ("com.google.android.talk".equals(callingPackage) - || "com.google.android.quicksearchbox".equals(callingPackage) - || "com.google.android.googlequicksearchbox".equals(callingPackage) - ) { - return false; - } - // STOPSHIP Change it to 'e'. - Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile " + Slog.w(TAG, message + " by " + callingPackage + " for another profile " + targetUserId + " from " + callingUserId); - return false; } @@ -445,8 +435,8 @@ public class LauncherAppsService extends SystemService { @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, - String packageName, List shortcutIds, ComponentName componentName, Intent intent, - int flags, UserHandle targetUser) { + String packageName, List shortcutIds, ComponentName componentName, int flags, + UserHandle targetUser) { ensureShortcutPermission(callingPackage); if (!canAccessProfile(callingPackage, targetUser, "Cannot get shortcuts") || !isUserEnabled(targetUser)) { @@ -457,17 +447,11 @@ public class LauncherAppsService extends SystemService { "To query by shortcut ID, package name must also be set"); } - if ((flags & ShortcutQuery.FLAG_MATCH_CHOOSER) == 0 - && intent != null) { - throw new IllegalArgumentException("Supplied an intent in the query, but did " - + "not request chooser targets"); - } - // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below. return new ParceledListSlice<>((List<ShortcutInfo>) mShortcutServiceInternal.getShortcuts(getCallingUserId(), callingPackage, changedSince, packageName, shortcutIds, - componentName, intent, flags, targetUser.getIdentifier())); + componentName, flags, targetUser.getIdentifier())); } @Override @@ -915,7 +899,6 @@ public class LauncherAppsService extends SystemService { cookie.packageName, /* changedSince= */ 0, packageName, /* shortcutIds=*/ null, /* component= */ null, - /* intent= */ null, ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY | ShortcutQuery.FLAG_GET_ALL_KINDS , userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0d9b052be7dd..d89b12e35215 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -569,7 +569,7 @@ public class PackageManagerService extends IPackageManager.Stub { Manifest.permission.RECEIVE_MMS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_NUMBER, + Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.ANSWER_PHONE_CALLS); @@ -2385,6 +2385,10 @@ public class PackageManagerService extends IPackageManager.Stub { final VersionInfo ver = mSettings.getInternalVersion(); mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + if (mIsUpgrade) { + logCriticalInfo(Log.INFO, + "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); + } // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = @@ -3036,13 +3040,14 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + final int callingUid = Binder.getCallingUid(); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE); final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, - resolveFlags, UserHandle.USER_SYSTEM); + resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); if (N == 0) { @@ -4021,12 +4026,12 @@ public class PackageManagerService extends IPackageManager.Stub { * action and a {@code android.intent.category.BROWSABLE} category</li> * </ul> */ - int updateFlagsForResolve(int flags, int userId, Intent intent, boolean includeInstantApp) { + int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, + boolean includeInstantApps) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } - final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; @@ -4038,7 +4043,7 @@ public class PackageManagerService extends IPackageManager.Stub { || callingUid == Process.SHELL_UID || callingUid == 0; final boolean allowMatchInstant = - (includeInstantApp + (includeInstantApps && Intent.ACTION_VIEW.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_BROWSABLE) && hasWebURI(intent)) @@ -5588,22 +5593,23 @@ public class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal( - intent, resolvedType, flags, userId, false /*includeInstantApp*/); + intent, resolvedType, flags, userId, false /*includeInstantApps*/); } private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, - int flags, int userId, boolean includeInstantApp) { + int flags, int userId, boolean includeInstantApps) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, - flags, userId, includeInstantApp); + flags, userId, includeInstantApps); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = @@ -5623,9 +5629,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) { return null; } + final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); - final int flags = updateFlagsForResolve(0, userId, intent, false); + final int flags = updateFlagsForResolve( + 0, userId, intent, callingUid, false /*includeInstantApps*/); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { @@ -5914,7 +5922,9 @@ public class PackageManagerService extends IPackageManager.Stub { List<ResolveInfo> query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve( + flags, userId, intent, callingUid, false /*includeInstantApps*/); intent = updateIntentForResolve(intent); // writer synchronized (mPackages) { @@ -6080,9 +6090,11 @@ public class PackageManagerService extends IPackageManager.Stub { } if (hasWebURI(intent)) { // cross-profile app linking works only towards the parent. + final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { - int flags = updateFlagsForResolve(0, parent.id, intent, false); + int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, + false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; @@ -6149,11 +6161,12 @@ public class PackageManagerService extends IPackageManager.Stub { } private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, - String resolvedType, int flags, int userId, boolean includeInstantApp) { + String resolvedType, int flags, int userId, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid()); - flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + final String instantAppPkgName = getInstantAppPackageName(callingUid); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); ComponentName comp = intent.getComponent(); @@ -6783,9 +6796,11 @@ public class PackageManagerService extends IPackageManager.Stub { Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); - enforceCrossUserPermission(Binder.getCallingUid(), userId, - false /* requireFullPermission */, false /* checkShell */, + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); + enforceCrossUserPermission(callingUid, userId, + false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); @@ -6963,7 +6978,9 @@ public class PackageManagerService extends IPackageManager.Stub { private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -6999,9 +7016,17 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { + final int callingUid = Binder.getCallingUid(); + return resolveServiceInternal( + intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); + } + + private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, + int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, intent, false); - List<ResolveInfo> query = queryIntentServicesInternal(intent, resolvedType, flags, userId); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); + List<ResolveInfo> query = queryIntentServicesInternal( + intent, resolvedType, flags, userId, callingUid, includeInstantApps); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, @@ -7015,14 +7040,17 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { - return new ParceledListSlice<>( - queryIntentServicesInternal(intent, resolvedType, flags, userId)); + final int callingUid = Binder.getCallingUid(); + return new ParceledListSlice<>(queryIntentServicesInternal( + intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, - String resolvedType, int flags, int userId) { + String resolvedType, int flags, int userId, int callingUid, + boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final String instantAppPkgName = getInstantAppPackageName(callingUid); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -7034,9 +7062,27 @@ public class PackageManagerService extends IPackageManager.Stub { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { - final ResolveInfo ri = new ResolveInfo(); - ri.serviceInfo = si; - list.add(ri); + // When specifying an explicit component, we prevent the service from being + // used when either 1) the service is in an instant application and the + // caller is not the same instant application or 2) the calling package is + // ephemeral and the activity is not visible to ephemeral applications. + final boolean matchVisibleToInstantAppOnly = + (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; + final boolean isCallerInstantApp = + instantAppPkgName != null; + final boolean isTargetSameInstantApp = + comp.getPackageName().equals(instantAppPkgName); + final boolean isTargetHiddenFromInstantApp = + (si.flags & ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0; + final boolean blockResolution = + !isTargetSameInstantApp + && ((matchVisibleToInstantAppOnly && isCallerInstantApp + && isTargetHiddenFromInstantApp)); + if (!blockResolution) { + final ResolveInfo ri = new ResolveInfo(); + ri.serviceInfo = si; + list.add(ri); + } } return list; } @@ -7045,17 +7091,67 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { - return mServices.queryIntent(intent, resolvedType, flags, userId); + return applyPostServiceResolutionFilter( + mServices.queryIntent(intent, resolvedType, flags, userId), + instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services, - userId); + return applyPostServiceResolutionFilter( + mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services, + userId), + instantAppPkgName); } return Collections.emptyList(); } } + private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos, + String instantAppPkgName) { + // TODO: When adding on-demand split support for non-instant apps, remove this check + // and always apply post filtering + if (instantAppPkgName == null) { + return resolveInfos; + } + for (int i = resolveInfos.size() - 1; i >= 0; i--) { + final ResolveInfo info = resolveInfos.get(i); + final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); + // allow services that are defined in the provided package + if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { + if (info.serviceInfo.splitName != null + && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, + info.serviceInfo.splitName)) { + // requested service is defined in a split that hasn't been installed yet. + // add the installer to the resolve list + if (DEBUG_EPHEMERAL) { + Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); + } + final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( + info.serviceInfo.packageName, info.serviceInfo.splitName, + info.serviceInfo.applicationInfo.versionCode); + // make sure this resolver is the default + installerInfo.isDefault = true; + installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + // add a non-generic filter + installerInfo.filter = new IntentFilter(); + // load resources from the correct package + installerInfo.resolvePackageName = info.getComponentInfo().packageName; + resolveInfos.set(i, installerInfo); + } + continue; + } + // allow services that have been explicitly exposed to ephemeral apps + if (!isEphemeralApp + && ((info.serviceInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0)) { + continue; + } + resolveInfos.remove(i); + } + return resolveInfos; + } + @Override public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { @@ -7066,7 +7162,9 @@ public class PackageManagerService extends IPackageManager.Stub { private @NonNull List<ResolveInfo> queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = updateFlagsForResolve(flags, userId, intent, false); + final int callingUid = Binder.getCallingUid(); + flags = updateFlagsForResolve(flags, userId, intent, callingUid, + false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -12401,11 +12499,29 @@ public class PackageManagerService extends IPackageManager.Stub { if (ps == null) { return null; } + final PackageUserState userState = ps.readUserState(userId); ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags, - ps.readUserState(userId), userId); + userState, userId); if (si == null) { return null; } + final boolean matchVisibleToInstantApp = + (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; + final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0; + // throw out filters that aren't visible to ephemeral apps + if (matchVisibleToInstantApp + && !(info.isVisibleToInstantApp() || userState.instantApp)) { + return null; + } + // throw out ephemeral filters if we're not explicitly requesting them + if (!isInstantApp && userState.instantApp) { + return null; + } + // throw out instant app filters if updates are available; will trigger + // instant app resolution + if (userState.instantApp && ps.isUpdateAvailable()) { + return null; + } final ResolveInfo res = new ResolveInfo(); res.serviceInfo = si; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { @@ -23135,10 +23251,18 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } + @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal( - intent, resolvedType, flags, userId, true /*includeInstantApp*/); + intent, resolvedType, flags, userId, true /*includeInstantApps*/); + } + + @Override + public ResolveInfo resolveService(Intent intent, String resolvedType, + int flags, int userId, int callingUid) { + return resolveServiceInternal( + intent, resolvedType, flags, userId, callingUid, true /*includeInstantApps*/); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 8739089552e4..6fb056a48f7e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3375,7 +3375,7 @@ final class Settings { private void applyDefaultPreferredActivityLPw(PackageManagerService service, Intent intent, int flags, ComponentName cn, String scheme, PatternMatcher ssp, IntentFilter.AuthorityEntry auth, PatternMatcher path, int userId) { - flags = service.updateFlagsForResolve(flags, userId, intent, false); + flags = service.updateFlagsForResolve(flags, userId, intent, Binder.getCallingUid(), false); List<ResolveInfo> ri = service.mActivities.queryIntent(intent, intent.getType(), flags, 0); if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Queried " + intent diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 6f7e0de4b8ee..5035e6820be8 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; @@ -32,7 +31,6 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.ShortcutOperation; @@ -70,9 +68,6 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String TAG_EXTRAS = "extras"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_CATEGORIES = "categories"; - private static final String TAG_CHOOSER_EXTRAS = "chooser-extras"; - private static final String TAG_CHOOSER_INTENT_FILTERS = "chooser-intent-filters"; - private static final String TAG_CHOOSER_COMPONENT_NAMES = "chooser-component-names"; private static final String ATTR_NAME = "name"; private static final String ATTR_CALL_COUNT = "call-count"; @@ -96,7 +91,6 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ICON_RES_ID = "icon-res"; private static final String ATTR_ICON_RES_NAME = "icon-resname"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; - private static final String ATTR_COMPONENT_NAMES = "component-names"; private static final String NAME_CATEGORIES = "categories"; @@ -206,7 +200,7 @@ class ShortcutPackage extends ShortcutPackageItem { if (shortcut != null) { mShortcutUser.mService.removeIcon(getPackageUserId(), shortcut); shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED - | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CHOOSER); + | ShortcutInfo.FLAG_MANIFEST); } return shortcut; } @@ -232,7 +226,7 @@ class ShortcutPackage extends ShortcutPackageItem { Preconditions.checkArgument(newShortcut.isEnabled(), "add/setDynamicShortcuts() cannot publish disabled shortcuts"); - addCorrectDynamicFlags(newShortcut); + newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); @@ -256,17 +250,6 @@ class ShortcutPackage extends ShortcutPackageItem { addShortcutInner(newShortcut); } - // TODO: Sample code & JavaDoc for ShortcutManager needs updating to reflect the fact that - // Chooser shortcuts are not always dynamic. - public void addCorrectDynamicFlags(@NonNull ShortcutInfo shortcut) { - if (shortcut.getIntent() != null) { - shortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); - } - if (!ArrayUtils.isEmpty(shortcut.getChooserIntentFilters())) { - shortcut.addFlags(ShortcutInfo.FLAG_CHOOSER); - } - } - /** * Remove all shortcuts that aren't pinned nor dynamic. */ @@ -299,11 +282,11 @@ class ShortcutPackage extends ShortcutPackageItem { boolean changed = false; for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - if (si.isDynamic() || si.isChooser()) { + if (si.isDynamic()) { changed = true; si.setTimestamp(now); - si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER); + si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); si.setRank(0); // It may still be pinned, so clear the rank. } } @@ -372,8 +355,7 @@ class ShortcutPackage extends ShortcutPackageItem { if (oldShortcut.isPinned()) { oldShortcut.setRank(0); - oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST - | ShortcutInfo.FLAG_CHOOSER); + oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST); if (disable) { oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED); } @@ -1133,8 +1115,8 @@ class ShortcutPackage extends ShortcutPackageItem { // Don't adjust ranks for manifest shortcuts. continue; } - // At this point, it must be dynamic or a chooser. - if (!si.isDynamicOrChooser()) { + // At this point, it must be dynamic. + if (!si.isDynamic()) { s.wtf("Non-dynamic shortcut found."); continue; } @@ -1311,7 +1293,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags() & ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES - | ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER)); + | ShortcutInfo.FLAG_DYNAMIC)); } else { // When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored // as dynamic. @@ -1334,36 +1316,15 @@ class ShortcutPackage extends ShortcutPackageItem { } final Intent[] intentsNoExtras = si.getIntentsNoExtras(); final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases(); - if (intentsNoExtras != null) { - final int numIntents = intentsNoExtras.length; - for (int i = 0; i < numIntents; i++) { - out.startTag(null, TAG_INTENT); - ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]); - ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]); - out.endTag(null, TAG_INTENT); - } - } - ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); - - ShortcutService.writeTagExtra(out, TAG_CHOOSER_EXTRAS, si.getChooserExtras()); - - final IntentFilter[] intentFilters = si.getChooserIntentFilters(); - if (intentFilters != null) { - for (int i = 0; i < intentFilters.length; i++) { - out.startTag(null, TAG_CHOOSER_INTENT_FILTERS); - intentFilters[i].writeToXml(out); - out.endTag(null, TAG_CHOOSER_INTENT_FILTERS); - } + final int numIntents = intentsNoExtras.length; + for (int i = 0; i < numIntents; i++) { + out.startTag(null, TAG_INTENT); + ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]); + ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]); + out.endTag(null, TAG_INTENT); } - final ComponentName[] componentNames = si.getChooserComponentNames(); - if (componentNames != null) { - for (int i = 0; i < componentNames.length; i++) { - out.startTag(null, TAG_CHOOSER_COMPONENT_NAMES); - ShortcutService.writeAttr(out, ATTR_COMPONENT_NAMES, componentNames[i]); - out.endTag(null, TAG_CHOOSER_COMPONENT_NAMES); - } - } + ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); out.endTag(null, TAG_SHORTCUT); } @@ -1436,9 +1397,6 @@ class ShortcutPackage extends ShortcutPackageItem { String iconResName; String bitmapPath; ArraySet<String> categories = null; - PersistableBundle chooserExtras; - List<IntentFilter> chooserIntentFilters = new ArrayList<>(); - List<ComponentName> chooserComponentNames = new ArrayList<>(); id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, @@ -1499,18 +1457,6 @@ class ShortcutPackage extends ShortcutPackageItem { } } continue; - case TAG_CHOOSER_EXTRAS: - chooserExtras = PersistableBundle.restoreFromXml(parser); - continue; - case TAG_CHOOSER_COMPONENT_NAMES: - chooserComponentNames.add(ShortcutService.parseComponentNameAttribute(parser, - ATTR_ACTIVITY)); - continue; - case TAG_CHOOSER_INTENT_FILTERS: - IntentFilter toAdd = new IntentFilter(); - toAdd.readFromXml(parser); - chooserIntentFilters.add(toAdd); - continue; } throw ShortcutService.throwForInvalidTag(depth, tag); } @@ -1604,10 +1550,10 @@ class ShortcutPackage extends ShortcutPackageItem { // Verify each shortcut's status. for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - if (!(si.isDeclaredInManifest() || si.isDynamicOrChooser() || si.isPinned())) { + if (!(si.isDeclaredInManifest() || si.isDynamic() || si.isPinned())) { failed = true; Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() - + " is not manifest, dynamic, chooser or pinned."); + + " is not manifest, dynamic or pinned."); } if (si.isDeclaredInManifest() && si.isDynamic()) { failed = true; @@ -1649,11 +1595,6 @@ class ShortcutPackage extends ShortcutPackageItem { Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " has a dummy target activity"); } - if (si.getIntent() == null && !si.isChooser()) { - failed = true; - Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() - + " has a null intent, but is not a chooser"); - } } if (failed) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 9bf689527864..7c89e1ccb7c8 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -27,7 +27,6 @@ import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -65,7 +64,6 @@ import android.os.FileUtils; import android.os.Handler; import android.os.LocaleList; import android.os.Looper; -import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; @@ -1754,7 +1752,6 @@ public class ShortcutService extends IShortcutService.Stub { ps.clearAllImplicitRanks(); assignImplicitRanks(newShortcuts); - // TODO: Consider removing Chooser fields. If so, the FLAG_CHOOSER should be removed for (int i = 0; i < size; i++) { final ShortcutInfo source = newShortcuts.get(i); fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); @@ -1794,13 +1791,6 @@ public class ShortcutService extends IShortcutService.Stub { if (replacingIcon || source.hasStringResources()) { fixUpShortcutResourceNamesAndValues(target); } - - // While updating, we keep the dynamic flag as it previously was, but refresh the - // chooser flag. - // TODO: If we support clearing Chooser fields, we should also remove the flag. - if (target.getChooserIntentFilters() != null) { - target.addFlags(ShortcutInfo.FLAG_CHOOSER); - } } // Lastly, adjust the ranks. @@ -1864,7 +1854,6 @@ public class ShortcutService extends IShortcutService.Stub { return true; } - // TODO: Ensure non-launchable shortcuts can not be pinned @Override public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, IntentSender resultIntent, int userId) { @@ -2020,7 +2009,7 @@ public class ShortcutService extends IShortcutService.Stub { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, - ShortcutInfo::isDynamicOrChooser); + ShortcutInfo::isDynamic); } } @@ -2213,14 +2202,6 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { throwIfUserLockedL(userId); - // For the chooser, we just check is the system is calling. - // STOPSHIP: We need to implement a new permission here rather than this terrible check. - // The packageName check is to try to distinguish between when an actual - // launcher is making the call, and when it's the system. - if (isCallerSystem() && packageName.equals("android")) { - return true; - } - final ShortcutUser user = getUserShortcutsLocked(userId); // Always trust the cached component. @@ -2393,7 +2374,7 @@ public class ShortcutService extends IShortcutService.Stub { public List<ShortcutInfo> getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, @Nullable Intent intent, + @Nullable ComponentName componentName, int queryFlags, int userId) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); @@ -2415,13 +2396,13 @@ public class ShortcutService extends IShortcutService.Stub { if (packageName != null) { getShortcutsInnerLocked(launcherUserId, callingPackage, packageName, shortcutIds, changedSince, - componentName, intent, queryFlags, userId, ret, cloneFlag); + componentName, queryFlags, userId, ret, cloneFlag); } else { final List<String> shortcutIdsF = shortcutIds; getUserShortcutsLocked(userId).forAllPackages(p -> { getShortcutsInnerLocked(launcherUserId, callingPackage, p.getPackageName(), shortcutIdsF, changedSince, - componentName, intent, queryFlags, userId, ret, cloneFlag); + componentName, queryFlags, userId, ret, cloneFlag); }); } } @@ -2430,7 +2411,7 @@ public class ShortcutService extends IShortcutService.Stub { private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, - @Nullable ComponentName componentName, Intent intent, int queryFlags, + @Nullable ComponentName componentName, int queryFlags, int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { final ArraySet<String> ids = shortcutIds == null ? null : new ArraySet<>(shortcutIds); @@ -2455,15 +2436,6 @@ public class ShortcutService extends IShortcutService.Stub { return false; } } - if (intent != null - && !si.hasMatchingFilter(mContext.getContentResolver(), intent)) { - return false; - } - - if (((queryFlags & ShortcutQuery.FLAG_MATCH_CHOOSER) != 0) - && si.isChooser()) { - return true; - } if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0) && si.isDynamic()) { return true; @@ -3449,6 +3421,11 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; + dumpNoCheck(fd, pw, args); + } + + @VisibleForTesting + void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) { boolean checkin = false; boolean clear = false; if (args != null) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b507df0b8447..7a315ab972e0 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -5254,11 +5254,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - // Don't allow snapshots to influence SystemUI visibility flags. - // TODO: Revisit this once SystemUI flags for snapshots are handled correctly boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type < FIRST_SYSTEM_WINDOW - && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0; + && attrs.type < FIRST_SYSTEM_WINDOW; final int stackId = win.getStackId(); if (mTopFullscreenOpaqueWindowState == null && visible) { if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9c4e700c87d3..a60dae7c7914 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2671,11 +2671,11 @@ public final class PowerManagerService extends SystemService public void run() { synchronized (this) { if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { - ShutdownThread.rebootSafeMode(mContext, confirm); + ShutdownThread.rebootSafeMode(getUiContext(), confirm); } else if (haltMode == HALT_MODE_REBOOT) { - ShutdownThread.reboot(mContext, reason, confirm); + ShutdownThread.reboot(getUiContext(), reason, confirm); } else { - ShutdownThread.shutdown(mContext, reason, confirm); + ShutdownThread.shutdown(getUiContext(), reason, confirm); } } } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 841e2a158c4f..864e83ef1f86 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -79,7 +79,7 @@ public final class ShutdownThread extends Thread { private static final int SHUTDOWN_VIBRATE_MS = 500; // state tracking - private static Object sIsStartedGuard = new Object(); + private static final Object sIsStartedGuard = new Object(); private static boolean sIsStarted = false; private static boolean mReboot; @@ -121,7 +121,8 @@ public final class ShutdownThread extends Thread { * state etc. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param confirm true if user confirmation is needed before shutting down. */ @@ -132,7 +133,11 @@ public final class ShutdownThread extends Thread { shutdownInner(context, confirm); } - static void shutdownInner(final Context context, boolean confirm) { + private static void shutdownInner(final Context context, boolean confirm) { + // ShutdownThread is called from many places, so best to verify here that the context passed + // in is themed. + context.assertRuntimeOverlayThemable(); + // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { @@ -204,7 +209,8 @@ public final class ShutdownThread extends Thread { * state etc. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param confirm true if user confirmation is needed before shutting down. */ @@ -220,7 +226,8 @@ public final class ShutdownThread extends Thread { * Request a reboot into safe mode. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param confirm true if user confirmation is needed before shutting down. */ public static void rebootSafeMode(final Context context, boolean confirm) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 212bd61dbc9a..218d21826d93 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -16,6 +16,7 @@ package com.android.server.statusbar; +import android.app.ActivityThread; import android.app.StatusBarManager; import android.content.ComponentName; import android.content.Context; @@ -61,6 +62,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { private static final boolean SPEW = false; private final Context mContext; + private final WindowManagerService mWindowManager; private Handler mHandler = new Handler(); private NotificationDelegate mNotificationDelegate; @@ -777,10 +779,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub { long identity = Binder.clearCallingIdentity(); try { mHandler.post(() -> { + // ShutdownThread displays UI, so give it a UI context. + Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); if (safeMode) { - ShutdownThread.rebootSafeMode(mContext, false); + ShutdownThread.rebootSafeMode(uiContext, false); } else { - ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, false); + ShutdownThread.reboot(uiContext, PowerManager.SHUTDOWN_USER_REQUESTED, false); } }); } finally { diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java index 963a5723b433..40bb4961a2bf 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java +++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java @@ -17,6 +17,8 @@ package com.android.server.statusbar; import android.content.ComponentName; import android.os.RemoteException; import android.os.ShellCommand; +import android.service.quicksettings.TileService; + import com.android.internal.statusbar.IStatusBarService; import java.io.PrintWriter; @@ -48,6 +50,10 @@ public class StatusBarShellCommand extends ShellCommand { return runRemoveTile(); case "click-tile": return runClickTile(); + case "check-support": + final PrintWriter pw = getOutPrintWriter(); + pw.println(String.valueOf(TileService.isQuickSettingsSupported())); + return 0; default: return handleDefaultCommands(cmd); } @@ -113,5 +119,8 @@ public class StatusBarShellCommand extends ShellCommand { pw.println(" click-tile COMPONENT"); pw.println(" Click on a TileService of the specified component"); pw.println(""); + pw.println(" check-support"); + pw.println(" Check if this device supports QS + APIs"); + pw.println(""); } } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 4b4be40880ee..2bc3c5f9abba 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -566,7 +566,7 @@ public class AppWindowContainerController return false; } - mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot()); + mContainer.startingData = new SnapshotStartingData(mService, snapshot); scheduleAddStartingWindow(); return true; } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index 62414e50d0c6..9f0ed2100462 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -32,7 +31,6 @@ import android.util.ArrayMap; import android.util.Slog; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import android.view.WindowManagerInternal; /** @@ -111,10 +109,15 @@ public class BoundsAnimationController { private final boolean mMoveToFullScreen; // True if this this animation was cancelled and will be replaced the another animation from // the same {@link #AnimateBoundsUser} target. - private boolean mWillReplace; - // True to true if this animation replaced a previous animation of the same + private boolean mSkipAnimationEnd; + // True if this animation replaced a previous animation of the same // {@link #AnimateBoundsUser} target. - private final boolean mReplacement; + private final boolean mSkipAnimationStart; + // 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; // Depending on whether we are animating from // a smaller to a larger size @@ -122,13 +125,14 @@ public class BoundsAnimationController { private final int mFrozenTaskHeight; BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen, - boolean replacement) { + boolean replacingExistingAnimation, boolean animatingToNewFullscreenState) { super(); mTarget = target; mFrom.set(from); mTo.set(to); mMoveToFullScreen = moveToFullScreen; - mReplacement = replacement; + mSkipAnimationStart = replacingExistingAnimation; + mAnimatingToNewFullscreenState = animatingToNewFullscreenState; addUpdateListener(this); addListener(this); @@ -145,11 +149,32 @@ public class BoundsAnimationController { } } - boolean animatingToLargerSize() { - if (mFrom.width() * mFrom.height() > mTo.width() * mTo.height()) { - return false; + @Override + public void onAnimationStart(Animator animation) { + if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget + + " mSkipAnimationStart=" + mSkipAnimationStart); + mFinishAnimationAfterTransition = false; + mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, + mFrom.top + mFrozenTaskHeight); + + // Ensure that we have prepared the target for animation before + // 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) { + mTarget.updatePictureInPictureMode(mMoveToFullScreen ? null : mTo); + } + + // 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); } - return true; } @Override @@ -174,32 +199,11 @@ public class BoundsAnimationController { } } - - @Override - public void onAnimationStart(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget - + " mReplacement=" + mReplacement); - mFinishAnimationAfterTransition = false; - // Ensure that we have prepared the target for animation before - // we trigger any size changes, so it can swap surfaces - // in to appropriate modes, or do as it wishes otherwise. - if (!mReplacement) { - mTarget.onAnimationStart(mMoveToFullScreen); - } - - // 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()) { - mTmpRect.set(mFrom.left, mFrom.top, - mFrom.left + mFrozenTaskWidth, mFrom.top + mFrozenTaskHeight); - mTarget.setPinnedStackSize(mFrom, mTmpRect); - } - } - @Override public void onAnimationEnd(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " mMoveToFullScreen=" + mMoveToFullScreen + " mWillReplace=" + mWillReplace); + + " mMoveToFullScreen=" + mMoveToFullScreen + + " mSkipAnimationEnd=" + mSkipAnimationEnd); // There could be another animation running. For example in the // move to fullscreen case, recents will also be closing while the @@ -214,7 +218,7 @@ public class BoundsAnimationController { finishAnimation(); mTarget.setPinnedStackSize(mTo, null); - if (mMoveToFullScreen && !mWillReplace) { + if (mMoveToFullScreen && !mSkipAnimationEnd) { mTarget.moveToFullscreen(); } } @@ -226,20 +230,27 @@ public class BoundsAnimationController { @Override public void cancel() { - mWillReplace = true; + mSkipAnimationEnd = true; if (DEBUG) Slog.d(TAG, "cancel: willReplace mTarget=" + mTarget); super.cancel(); } /** Returns true if the animation target is the same as the input bounds. */ - public boolean isAnimatingTo(Rect 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 (!mWillReplace) { + if (!mSkipAnimationEnd) { mTarget.onAnimationEnd(); } removeListener(this); @@ -249,7 +260,7 @@ public class BoundsAnimationController { @Override public void onAnimationRepeat(Animator animation) { - + // Do nothing } } @@ -266,14 +277,27 @@ public class BoundsAnimationController { boolean setSize(Rect bounds); /** * Behaves as setSize, 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. + * to allow for more flexibility during resizing. Only works for the pinned stack at the + * moment. */ 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. */ @@ -286,9 +310,12 @@ public class BoundsAnimationController { 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); + + " moveToFullscreen=" + moveToFullscreen + " replacing=" + replacing + + " animatingToNewFullscreenState=" + animatingToNewFullscreenState); if (replacing) { if (existing.isAnimatingTo(to)) { @@ -300,8 +327,8 @@ public class BoundsAnimationController { } existing.cancel(); } - final BoundsAnimator animator = - new BoundsAnimator(target, from, to, moveToFullscreen, replacing); + final BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen, + replacing, animatingToNewFullscreenState); mRunningAnimations.put(target, animator); animator.setFloatValues(0f, 1f); animator.setDuration((animationDuration != -1 ? animationDuration diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ae413e5eb9fd..4ebf1fcb7206 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -180,11 +180,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Mapping from a token IBinder to a WindowToken object on this display. private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap(); + // Initial display metrics. int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mInitialDisplayDensity = 0; + + /** + * Overridden display size. Initialized with {@link #mInitialDisplayWidth} + * and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size". + * @see WindowManagerService#setForcedDisplaySize(int, int, int) + */ int mBaseDisplayWidth = 0; int mBaseDisplayHeight = 0; + /** + * Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity} + * but can be set from Settings or via shell command "adb shell wm density". + * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int) + */ int mBaseDisplayDensity = 0; boolean mDisplayScalingDisabled; private final DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -1134,6 +1146,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo config.screenHeightDp = (int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation, config.uiMode, mDisplayId) / mDisplayMetrics.density); + + mService.mPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh, mTmpRect); + final int leftInset = mTmpRect.left; + final int topInset = mTmpRect.top; + // appBounds at the root level should mirror the app screen size. + config.setAppBounds(leftInset /*left*/, topInset /*top*/, leftInset + displayInfo.appWidth /*right*/, + topInset + displayInfo.appHeight /*bottom*/); final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90 || displayInfo.rotation == Surface.ROTATION_270); @@ -1490,8 +1509,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } void updateDisplayInfo() { + // Check if display metrics changed and update base values if needed. + updateBaseDisplayMetricsIfNeeded(); + mDisplay.getDisplayInfo(mDisplayInfo); mDisplay.getMetrics(mDisplayMetrics); + for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { mTaskStackContainers.get(i).updateDisplayInfo(null); } @@ -1507,10 +1530,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; - mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; - mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; - mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); + updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, + mDisplayInfo.logicalDensityDpi); + mInitialDisplayWidth = mDisplayInfo.logicalWidth; + mInitialDisplayHeight = mDisplayInfo.logicalHeight; + mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; } void getLogicalDisplayRect(Rect out) { @@ -1540,6 +1564,42 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + /** + * If display metrics changed, overrides are not set and it's not just a rotation - update base + * values. + */ + private void updateBaseDisplayMetricsIfNeeded() { + // Get real display metrics without overrides from WM. + mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo); + final int orientation = mDisplayInfo.rotation; + final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); + final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth; + final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight; + final int newDensity = mDisplayInfo.logicalDensityDpi; + + final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth + || mInitialDisplayHeight != newHeight + || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi; + + if (displayMetricsChanged) { + // Check if display size or density is forced. + final boolean isDisplaySizeForced = mBaseDisplayWidth != mInitialDisplayWidth + || mBaseDisplayHeight != mInitialDisplayHeight; + final boolean isDisplayDensityForced = mBaseDisplayDensity != mInitialDisplayDensity; + + // If there is an override set for base values - use it, otherwise use new values. + updateBaseDisplayMetrics(isDisplaySizeForced ? mBaseDisplayWidth : newWidth, + isDisplaySizeForced ? mBaseDisplayHeight : newHeight, + isDisplayDensityForced ? mBaseDisplayDensity : newDensity); + + // Real display metrics changed, so we should also update initial values. + mInitialDisplayWidth = newWidth; + mInitialDisplayHeight = newHeight; + mInitialDisplayDensity = newDensity; + mService.reconfigureDisplayLocked(this); + } + } + /** Sets the maximum width the screen resolution can be */ void setMaxUiWidth(int width) { if (DEBUG_DISPLAY) { @@ -2860,7 +2920,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (stack != null) { stack.getBounds(frame); } - } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) { + + // We want to screenshot with the exact bounds of the surface of the app. Thus, + // intersect it with the frame. + frame.intersect(w.mFrame); + }else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) { final Rect wf = w.mFrame; final Rect cr = w.mContentInsets; int left = wf.left + cr.left; diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 85eae0212373..e3002563a6fd 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -225,14 +225,25 @@ public class DockedStackDividerController implements DimLayerUser { mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect); config.unset(); config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + + final int displayId = mDisplayContent.getDisplayId(); + final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, + baseConfig.uiMode, displayId); + final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, + baseConfig.uiMode, displayId); + mService.mPolicy.getNonDecorInsetsLw(rotation, dw, dh, mTmpRect); + final int leftInset = mTmpRect.left; + final int topInset = mTmpRect.top; + + config.setAppBounds(leftInset /*left*/, topInset /*top*/, leftInset + appWidth /*right*/, + topInset + appHeight /*bottom*/); + config.screenWidthDp = (int) (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode, - mDisplayContent.getDisplayId()) / - mDisplayContent.getDisplayMetrics().density); + displayId) / mDisplayContent.getDisplayMetrics().density); config.screenHeightDp = (int) (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode, - mDisplayContent.getDisplayId()) / - mDisplayContent.getDisplayMetrics().density); + displayId) / mDisplayContent.getDisplayMetrics().density); final Context rotationContext = mService.mContext.createConfigurationContext(config); mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm( rotationContext.getResources(), dw, dh, getContentWidth(), diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java index e73d4d2559fb..35f35db5ada3 100644 --- a/services/core/java/com/android/server/wm/SnapshotStartingData.java +++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.GraphicBuffer; import android.view.WindowManagerPolicy.StartingSurface; @@ -25,9 +26,9 @@ import android.view.WindowManagerPolicy.StartingSurface; class SnapshotStartingData extends StartingData { private final WindowManagerService mService; - private final GraphicBuffer mSnapshot; + private final TaskSnapshot mSnapshot; - SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) { + SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) { super(service); mService = service; mSnapshot = snapshot; diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index 8186d30a55ff..635527e68ffe 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -270,6 +270,12 @@ public class StackWindowController int width; int height; + + final Rect parentAppBounds = parentConfig.appBounds; + + config.setAppBounds(!bounds.isEmpty() ? bounds : null); + boolean intersectParentBounds = false; + if (StackId.tasksAreFloating(mStackId)) { // Floating tasks should not be resized to the screen's bounds. @@ -280,6 +286,7 @@ public class StackWindowController // the fullscreen stack, without intersecting it with the display bounds stableBounds.inset(mTmpStableInsets); nonDecorBounds.inset(mTmpNonDecorInsets); + intersectParentBounds = true; } width = (int) (stableBounds.width() / density); height = (int) (stableBounds.height() / density); @@ -299,6 +306,11 @@ public class StackWindowController parentConfig.screenWidthDp); height = Math.min((int) (stableBounds.height() / density), parentConfig.screenHeightDp); + intersectParentBounds = true; + } + + if (intersectParentBounds && config.appBounds != null) { + config.appBounds.intersect(parentAppBounds); } config.screenWidthDp = width; @@ -350,6 +362,13 @@ public class StackWindowController } } + /** Calls directly into activity manager so window manager lock shouldn't held. */ + public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) { + if (mListener != null) { + mListener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds); + } + } + void requestResize(Rect bounds) { mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java index c763c175687f..a55f9dfa2d60 100644 --- a/services/core/java/com/android/server/wm/StackWindowListener.java +++ b/services/core/java/com/android/server/wm/StackWindowListener.java @@ -26,4 +26,10 @@ public interface StackWindowListener extends WindowContainerListener { /** Called when the stack container would like its controller to resize. */ void requestResize(Rect bounds); + + /** + * Called when the stack container pinned stack animation will change the picture-in-picture + * mode. This is a direct call into ActivityManager. + */ + default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {} } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3ffb093ba386..b816d8199aa6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -17,15 +17,14 @@ package com.android.server.wm; import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static com.android.server.EventLogTags.WM_TASK_REMOVED; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -40,7 +39,6 @@ import android.view.DisplayInfo; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.EventLogTags; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index b8d0b8c096fe..48b01f40fc65 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -28,6 +28,7 @@ import android.app.ActivityManager.StackId; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; import android.graphics.GraphicBuffer; +import android.graphics.Rect; import android.os.Environment; import android.util.ArraySet; import android.view.WindowManagerPolicy.StartingSurface; @@ -152,7 +153,7 @@ class TaskSnapshotController { * MANAGER LOCK WHEN CALLING THIS METHOD! */ StartingSurface createStartingSurface(AppWindowToken token, - GraphicBuffer snapshot) { + TaskSnapshot snapshot) { return TaskSnapshotSurface.create(mService, token, snapshot); } @@ -166,8 +167,17 @@ class TaskSnapshotController { if (buffer == null) { return null; } + final WindowState mainWindow = top.findMainWindow(); return new TaskSnapshot(buffer, top.getConfiguration().orientation, - top.findMainWindow().mStableInsets, false /* reduced */, 1f /* scale */); + minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), false /* reduced */, + 1f /* scale */); + } + + private Rect minRect(Rect rect1, Rect rect2) { + return new Rect(Math.min(rect1.left, rect2.left), + Math.min(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom)); } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 04403e2712c1..1591e480af4f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -16,20 +16,35 @@ package com.android.server.wm; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.graphics.Color.WHITE; +import static android.graphics.Color.alpha; +import static android.view.SurfaceControl.HIDDEN; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; +import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; +import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SCALED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; +import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; +import static com.android.internal.policy.DecorView.getColorViewLeftInset; +import static com.android.internal.policy.DecorView.getColorViewTopInset; +import static com.android.internal.policy.DecorView.getNavigationBarRect; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager.TaskDescription; -import android.graphics.Bitmap; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.Rect; @@ -37,17 +52,22 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.util.MergedConfiguration; import android.util.Slog; import android.view.IWindowSession; import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy.StartingSurface; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.DecorView; import com.android.internal.view.BaseIWindow; /** @@ -57,19 +77,57 @@ import com.android.internal.view.BaseIWindow; */ class TaskSnapshotSurface implements StartingSurface { + private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450; + + /** + * When creating the starting window, we use the exact same layout flags such that we end up + * with a window with the exact same dimensions etc. However, these flags are not used in layout + * and might cause other side effects so we exclude them. + */ + private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE + | FLAG_NOT_TOUCHABLE + | FLAG_NOT_TOUCH_MODAL + | FLAG_ALT_FOCUSABLE_IM + | FLAG_NOT_FOCUSABLE + | FLAG_HARDWARE_ACCELERATED + | FLAG_IGNORE_CHEEK_PRESSES + | FLAG_LOCAL_FOCUS_MODE + | FLAG_SLIPPERY + | FLAG_WATCH_OUTSIDE_TOUCH + | FLAG_SPLIT_TOUCH + | FLAG_SCALED + | FLAG_SECURE; + private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; private static final int MSG_REPORT_DRAW = 0; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; private final Window mWindow; private final Surface mSurface; + private SurfaceControl mChildSurfaceControl; private final IWindowSession mSession; private final WindowManagerService mService; + private final Rect mTaskBounds; + private final Rect mStableInsets = new Rect(); + private final Rect mContentInsets = new Rect(); + private final Rect mFrame = new Rect(); + private final TaskSnapshot mSnapshot; + private final CharSequence mTitle; private boolean mHasDrawn; private boolean mReportNextDraw; - private Paint mFillBackgroundPaint = new Paint(); + private long mShownTime; + private final Handler mHandler; + private final boolean mSizeMismatch; + private final Paint mBackgroundPaint = new Paint(); + private final Paint mStatusBarPaint = new Paint(); + private final Paint mNavigationBarPaint = new Paint(); + private final int mStatusBarColor; + private final int mNavigationBarColor; + private final int mSysUiVis; + private final int mWindowFlags; + private final int mWindowPrivateFlags; static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, - GraphicBuffer snapshot) { + TaskSnapshot snapshot) { final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); final Window window = new Window(); @@ -78,32 +136,51 @@ class TaskSnapshotSurface implements StartingSurface { final Surface surface = new Surface(); final Rect tmpRect = new Rect(); final Rect tmpFrame = new Rect(); + final Rect taskBounds; + final Rect tmpContentInsets = new Rect(); + final Rect tmpStableInsets = new Rect(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - int fillBackgroundColor = Color.WHITE; + int backgroundColor = WHITE; + int statusBarColor = 0; + int navigationBarColor = 0; + final int sysUiVis; + final int windowFlags; + final int windowPrivateFlags; synchronized (service.mWindowMap) { + final WindowState mainWindow = token.findMainWindow(); + if (mainWindow == null) { + Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token=" + + token); + return null; + } + sysUiVis = mainWindow.getSystemUiVisibility(); + windowFlags = mainWindow.getAttrs().flags; + windowPrivateFlags = mainWindow.getAttrs().privateFlags; + layoutParams.type = TYPE_APPLICATION_STARTING; - layoutParams.format = snapshot.getFormat(); - layoutParams.flags = FLAG_LAYOUT_INSET_DECOR - | FLAG_LAYOUT_IN_SCREEN + layoutParams.format = snapshot.getSnapshot().getFormat(); + layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) | FLAG_NOT_FOCUSABLE - | FLAG_NOT_TOUCHABLE - | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + | FLAG_NOT_TOUCHABLE; layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT; layoutParams.token = token.token; layoutParams.width = LayoutParams.MATCH_PARENT; layoutParams.height = LayoutParams.MATCH_PARENT; - - // TODO: Inherit behavior whether to draw behind status bar/nav bar. - layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + layoutParams.systemUiVisibility = sysUiVis; final Task task = token.getTask(); if (task != null) { - layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId)); + layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId)); final TaskDescription taskDescription = task.getTaskDescription(); if (taskDescription != null) { - fillBackgroundColor = taskDescription.getBackgroundColor(); + backgroundColor = taskDescription.getBackgroundColor(); + statusBarColor = taskDescription.getStatusBarColor(); + navigationBarColor = taskDescription.getNavigationBarColor(); } + taskBounds = new Rect(); + task.getBounds(taskBounds); + } else { + taskBounds = null; } } try { @@ -118,31 +195,57 @@ class TaskSnapshotSurface implements StartingSurface { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, - surface, fillBackgroundColor); + surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, + navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, - tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration, - surface); + tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, + tmpMergedConfiguration, surface); } catch (RemoteException e) { // Local call. } - snapshotSurface.drawSnapshot(snapshot); + snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets); + snapshotSurface.drawSnapshot(); return snapshotSurface; } @VisibleForTesting TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface, - int fillBackgroundColor) { + TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor, + int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags, + Rect taskBounds) { mService = service; + mHandler = new Handler(mService.mH.getLooper()); mSession = WindowManagerGlobal.getWindowSession(); mWindow = window; mSurface = surface; - mFillBackgroundPaint.setColor(fillBackgroundColor); + mSnapshot = snapshot; + mTitle = title; + mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); + mTaskBounds = taskBounds; + mSysUiVis = sysUiVis; + mWindowFlags = windowFlags; + mWindowPrivateFlags = windowPrivateFlags; + mSizeMismatch = (mFrame.width() != snapshot.getSnapshot().getWidth() + || mFrame.height() != snapshot.getSnapshot().getHeight()); + mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags, + service.mContext.getColor(R.color.system_bar_background_semi_transparent), + statusBarColor); + mNavigationBarColor = navigationBarColor; + mStatusBarPaint.setColor(mStatusBarColor); + mNavigationBarPaint.setColor(navigationBarColor); } @Override public void remove() { + synchronized (mService.mWindowMap) { + final long now = SystemClock.uptimeMillis(); + if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) { + mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); + return; + } + } try { mSession.remove(mWindow); } catch (RemoteException e) { @@ -150,31 +253,149 @@ class TaskSnapshotSurface implements StartingSurface { } } - private void drawSnapshot(GraphicBuffer snapshot) { - mSurface.attachAndQueueBuffer(snapshot); + @VisibleForTesting + void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) { + mFrame.set(frame); + mContentInsets.set(contentInsets); + mStableInsets.set(stableInsets); + } + + private void drawSnapshot() { + final GraphicBuffer buffer = mSnapshot.getSnapshot(); + if (mSizeMismatch) { + // The dimensions of the buffer and the window don't match, so attaching the buffer + // will fail. Better create a child window with the exact dimensions and fill the parent + // window with the background color! + drawSizeMismatchSnapshot(buffer); + } else { + drawSizeMatchSnapshot(buffer); + } final boolean reportNextDraw; synchronized (mService.mWindowMap) { + mShownTime = SystemClock.uptimeMillis(); mHasDrawn = true; reportNextDraw = mReportNextDraw; } if (reportNextDraw) { reportDrawn(); } + } + + private void drawSizeMatchSnapshot(GraphicBuffer buffer) { + mSurface.attachAndQueueBuffer(buffer); + mSurface.release(); + } + + private void drawSizeMismatchSnapshot(GraphicBuffer buffer) { + final SurfaceSession session = new SurfaceSession(mSurface); + + // Keep a reference to it such that it doesn't get destroyed when finalized. + mChildSurfaceControl = new SurfaceControl(session, + mTitle + " - task-snapshot-surface", + buffer.getWidth(), buffer.getHeight(), buffer.getFormat(), HIDDEN); + Surface surface = new Surface(); + surface.copyFrom(mChildSurfaceControl); + + // Clip off ugly navigation bar. + final Rect crop = calculateSnapshotCrop(); + final Rect frame = calculateSnapshotFrame(crop); + SurfaceControl.openTransaction(); + try { + // We can just show the surface here as it will still be hidden as the parent is + // still hidden. + mChildSurfaceControl.show(); + mChildSurfaceControl.setWindowCrop(crop); + mChildSurfaceControl.setPosition(frame.left, frame.top); + } finally { + SurfaceControl.closeTransaction(); + } + surface.attachAndQueueBuffer(buffer); + surface.release(); + + final Canvas c = mSurface.lockCanvas(null); + drawBackgroundAndBars(c, frame); + mSurface.unlockCanvasAndPost(c); mSurface.release(); } @VisibleForTesting - void fillEmptyBackground(Canvas c, Bitmap b) { - final boolean fillHorizontally = c.getWidth() > b.getWidth(); - final boolean fillVertically = c.getHeight() > b.getHeight(); + Rect calculateSnapshotCrop() { + final Rect rect = new Rect(); + rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); + final Rect insets = mSnapshot.getContentInsets(); + + // Let's remove all system decorations except the status bar, but only if the task is at the + // very top of the screen. + rect.inset(insets.left, mTaskBounds.top != 0 ? insets.top : 0, insets.right, insets.bottom); + return rect; + } + + @VisibleForTesting + Rect calculateSnapshotFrame(Rect crop) { + final Rect frame = new Rect(crop); + + // By default, offset it to to top/left corner + frame.offsetTo(-crop.left, -crop.top); + + // However, we also need to make space for the navigation bar on the left side. + final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, + mContentInsets.left); + frame.offset(colorViewLeftInset, 0); + return frame; + } + + @VisibleForTesting + void drawBackgroundAndBars(Canvas c, Rect frame) { + final int statusBarHeight = getStatusBarColorViewHeight(); + final boolean fillHorizontally = c.getWidth() > frame.right; + final boolean fillVertically = c.getHeight() > frame.bottom; if (fillHorizontally) { - c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically - ? b.getHeight() - : c.getHeight(), - mFillBackgroundPaint); + c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0, + c.getWidth(), fillVertically + ? frame.bottom + : c.getHeight(), + mBackgroundPaint); } if (fillVertically) { - c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint); + c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); + } + drawStatusBarBackground(c, frame, statusBarHeight); + drawNavigationBarBackground(c); + } + + private int getStatusBarColorViewHeight() { + final boolean forceStatusBarBackground = + (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) { + return getColorViewTopInset(mStableInsets.top, mContentInsets.top); + } else { + return 0; + } + } + + private boolean isNavigationBarColorViewVisible() { + return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */); + } + + @VisibleForTesting + void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) { + if (statusBarHeight > 0 && c.getWidth() > frame.right) { + final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right, + mContentInsets.right); + c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); + } + } + + @VisibleForTesting + void drawNavigationBarBackground(Canvas c) { + final Rect navigationBarRect = new Rect(); + getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, + navigationBarRect); + final boolean visible = isNavigationBarColorViewVisible(); + if (visible && !navigationBarRect.isEmpty()) { + c.drawRect(navigationBarRect, mNavigationBarPaint); } } @@ -211,10 +432,10 @@ class TaskSnapshotSurface implements StartingSurface { } }; - private static class Window extends BaseIWindow { + @VisibleForTesting + static class Window extends BaseIWindow { private TaskSnapshotSurface mOuter; - public void setOuter(TaskSnapshotSurface outer) { mOuter = outer; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 8b5f5acc5c28..1a67ac73021d 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1483,6 +1483,14 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye } @Override // AnimatesBounds + public void updatePictureInPictureMode(Rect targetStackBounds) { + final StackWindowController controller = getController(); + if (controller != null) { + controller.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds); + } + } + + @Override // AnimatesBounds public void onAnimationEnd() { synchronized (mService.mWindowMap) { mBoundsAnimating = false; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 95fbbb89a649..f1796de2d342 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -102,6 +102,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.IActivityManager; import android.content.BroadcastReceiver; @@ -3196,19 +3197,25 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { - ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void reboot(boolean confirm) { - ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void rebootSafeMode(boolean confirm) { - ShutdownThread.rebootSafeMode(mContext, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(), + confirm); } public void setCurrentProfileIds(final int[] currentProfileIds) { @@ -5312,8 +5319,8 @@ public class WindowManagerService extends IWindowManager.Stub if (displayContent.mBaseDisplayWidth != width || displayContent.mBaseDisplayHeight != height) { Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height); - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; + displayContent.updateBaseDisplayMetrics(width, height, + displayContent.mBaseDisplayDensity); } } catch (NumberFormatException ex) { } @@ -5338,8 +5345,7 @@ public class WindowManagerService extends IWindowManager.Stub // displayContent must not be null private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { Slog.i(TAG_WM, "Using new display size: " + width + "x" + height); - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; + displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity); reconfigureDisplayLocked(displayContent); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java new file mode 100644 index 000000000000..97fa9d552142 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java @@ -0,0 +1,211 @@ +/* + * 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.devicepolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; +import android.app.admin.IDeviceAdminService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.BackgroundThread; +import com.android.server.am.PersistentConnection; + +import java.io.PrintWriter; +import java.util.List; + +/** + * Manages connections to persistent services in owner packages. + */ +public class DeviceAdminServiceController { + static final String TAG = DevicePolicyManagerService.LOG_TAG; + + static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE. + + final Object mLock = new Object(); + final Context mContext; + + private final DevicePolicyManagerService mService; + private final DevicePolicyManagerService.Injector mInjector; + + private final Handler mHandler; // needed? + + static void debug(String format, Object... args) { + if (!DEBUG) { + return; + } + Slog.d(TAG, String.format(format, args)); + } + + private class DevicePolicyServiceConnection + extends PersistentConnection<IDeviceAdminService> { + public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) { + super(TAG, mContext, mHandler, userId, componentName); + } + + @Override + protected IDeviceAdminService asInterface(IBinder binder) { + return IDeviceAdminService.Stub.asInterface(binder); + } + } + + /** + * User-ID -> {@link PersistentConnection}. + */ + @GuardedBy("mLock") + private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>(); + + public DeviceAdminServiceController(DevicePolicyManagerService service) { + mService = service; + mInjector = service.mInjector; + mContext = mInjector.mContext; + mHandler = new Handler(BackgroundThread.get().getLooper()); + } + + /** + * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} + * in a given package. + */ + @Nullable + private ServiceInfo findService(@NonNull String packageName, int userId) { + final Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE); + intent.setPackage(packageName); + + try { + final ParceledListSlice<ResolveInfo> pls = mInjector.getIPackageManager() + .queryIntentServices(intent, null, /* flags=*/ 0, userId); + if (pls == null) { + return null; + } + final List<ResolveInfo> list = pls.getList(); + if (list.size() == 0) { + return null; + } + // Note if multiple services are found, that's an error, even if only one of them + // is exported. + if (list.size() > 1) { + Log.e(TAG, "More than one DeviceAdminService's found in package " + + packageName + + ". They'll all be ignored."); + return null; + } + final ServiceInfo si = list.get(0).serviceInfo; + if (si.exported) { + Log.e(TAG, "DeviceAdminService must not be exported: '" + + si.getComponentName().flattenToShortString() + + "' will be ignored."); + return null; + } + return si; + } catch (RemoteException e) { + } + return null; + } + + /** + * Find a service that handles {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} + * in an owner package and connect to it. + */ + public void startServiceForOwner(@NonNull String packageName, int userId, + @NonNull String actionForLog) { + final long token = mInjector.binderClearCallingIdentity(); + try { + synchronized (mLock) { + final ServiceInfo service = findService(packageName, userId); + if (service == null) { + debug("Owner package %s on u%d has no service.", + packageName, userId); + disconnectServiceOnUserLocked(userId, actionForLog); + return; + } + // See if it's already running. + final PersistentConnection<IDeviceAdminService> existing = + mConnections.get(userId); + if (existing != null) { + if (existing.getComponentName().equals(service.getComponentName())) { + return; + } + disconnectServiceOnUserLocked(userId, actionForLog); + } + + debug("Owner package %s on u%d has service %s for %s", + packageName, userId, + service.getComponentName().flattenToShortString(), actionForLog); + + final DevicePolicyServiceConnection conn = + new DevicePolicyServiceConnection( + userId, service.getComponentName()); + mConnections.put(userId, conn); + conn.connect(); + } + } finally { + mInjector.binderRestoreCallingIdentity(token); + } + } + + /** + * Stop an owner service on a given user. + */ + public void stopServiceForOwner(int userId, @NonNull String actionForLog) { + final long token = mInjector.binderClearCallingIdentity(); + try { + synchronized (mLock) { + disconnectServiceOnUserLocked(userId, actionForLog); + } + } finally { + mInjector.binderRestoreCallingIdentity(token); + } + } + + private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { + final DevicePolicyServiceConnection conn = mConnections.get(userId); + if (conn != null) { + debug("Stopping service for u%d if already running for %s.", + userId, actionForLog); + conn.disconnect(); + mConnections.remove(userId); + } + } + + public void dump(String prefix, PrintWriter pw) { + synchronized (mLock) { + if (mConnections.size() == 0) { + return; + } + pw.println(); + pw.print(prefix); pw.println("Owner Services:"); + for (int i = 0; i < mConnections.size(); i++) { + final int userId = mConnections.keyAt(i); + pw.print(prefix); pw.print(" "); pw.print("User: "); pw.println(userId); + + final DevicePolicyServiceConnection con = mConnections.valueAt(i); + con.dump(prefix + " ", pw); + } + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6f49324bfc56..bfa1b9988b2f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -140,7 +140,6 @@ import android.os.storage.StorageManager; import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; -import android.security.Credentials; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; @@ -194,7 +193,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.text.DateFormat; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -365,6 +363,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final UserManagerInternal mUserManagerInternal; final TelephonyManager mTelephonyManager; private final LockPatternUtils mLockPatternUtils; + private final DeviceAdminServiceController mDeviceAdminServiceController; /** * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p @@ -459,7 +458,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void onStartUser(int userHandle) { - mService.onStartUser(userHandle); + mService.handleStartUser(userHandle); + } + + @Override + public void onUnlockUser(int userHandle) { + mService.handleUnlockUser(userHandle); + } + + @Override + public void onStopUser(int userHandle) { + mService.handleStopUser(userHandle); } } @@ -1420,7 +1429,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void handlePackagesChanged(String packageName, int userHandle) { + private void handlePackagesChanged(@Nullable String packageName, int userHandle) { boolean removedAdmin = false; if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle); DevicePolicyData policy = getUserData(userHandle); @@ -1434,9 +1443,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (packageName == null || packageName.equals(adminPackage)) { if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null || mIPackageManager.getReceiverInfo(aa.info.getComponent(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - userHandle) == null) { + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + userHandle) == null) { removedAdmin = true; policy.mAdminList.remove(i); policy.mAdminMap.remove(aa.info.getComponent()); @@ -1461,6 +1470,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + // If it's an owner package, we may need to refresh the bound connection. + final ComponentName owner = getOwnerComponent(userHandle); + if ((packageName != null) && (owner != null) + && (owner.getPackageName().equals(packageName))) { + startOwnerService(userHandle, "package-broadcast"); + } + // Persist updates if the removed package was an admin or delegate. if (removedAdmin || removedDelegate) { saveSettingsLocked(policy.mUserHandle); @@ -1791,6 +1807,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Needed when mHasFeature == false, because it controls the certificate warning text. mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler); + mDeviceAdminServiceController = new DeviceAdminServiceController(this); + if (!mHasFeature) { // Skip the rest of the initialization return; @@ -2943,7 +2961,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { loadOwners(); cleanUpOldUsers(); ensureUnknownSourcesRestrictionForProfileOwners(); - onStartUser(UserHandle.USER_SYSTEM); + handleStartUser(UserHandle.USER_SYSTEM); // Register an observer for watching for user setup complete and settings changes. mSetupContentObserver.register(); @@ -2990,10 +3008,32 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void onStartUser(int userId) { + void handleStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); pushUserRestrictions(userId); + + startOwnerService(userId, "start-user"); + } + + void handleUnlockUser(int userId) { + startOwnerService(userId, "unlock-user"); + } + + void handleStopUser(int userId) { + stopOwnerService(userId, "stop-user"); + } + + private void startOwnerService(int userId, String actionForLog) { + final ComponentName owner = getOwnerComponent(userId); + if (owner != null) { + mDeviceAdminServiceController.startServiceForOwner( + owner.getPackageName(), userId, actionForLog); + } + } + + private void stopOwnerService(int userId, String actionForLog) { + mDeviceAdminServiceController.stopServiceForOwner(userId, actionForLog); } private void cleanUpOldUsers() { @@ -5078,7 +5118,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * @param callerPackage the name of the calling package. Required if {@code who} is * {@code null}. * @param reqPolicy the policy used in the API whose access permission is being checked. - * @param scoppe the delegation scope corresponding to the API being checked. + * @param scope the delegation scope corresponding to the API being checked. * @throws SecurityException if {@code who} is given and is not an owner for {@code reqPolicy}; * or when {@code who} is {@code null} and {@code callerPackage} is not a delegate * of {@code scope}. @@ -6460,6 +6500,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } + mDeviceAdminServiceController.startServiceForOwner( + admin.getPackageName(), userId, "set-device-owner"); + Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); return true; } @@ -6615,6 +6658,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) { + mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-device-owner"); + if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; @@ -6692,6 +6737,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(id); } + mDeviceAdminServiceController.startServiceForOwner( + who.getPackageName(), userHandle, "set-profile-owner"); return true; } } @@ -6723,6 +6770,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) { + mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-profile-owner"); + if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; @@ -7275,6 +7324,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { pw.println("Current Device Policy Manager state:"); mOwners.dump(" ", pw); + mDeviceAdminServiceController.dump(" ", pw); int userCount = mUserData.size(); for (int u = 0; u < userCount; u++) { DevicePolicyData policy = getUserData(mUserData.keyAt(u)); @@ -9624,6 +9674,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } + /** + * Return device owner or profile owner set on a given user. + */ + private @Nullable ComponentName getOwnerComponent(int userId) { + synchronized (this) { + if (mOwners.getDeviceOwnerUserId() == userId) { + return mOwners.getDeviceOwnerComponent(); + } + if (mOwners.hasProfileOwner(userId)) { + return mOwners.getProfileOwnerComponent(userId); + } + } + return null; + } + private int checkManagedUserProvisioningPreCondition(int callingUserId) { if (!hasFeatureManagedUsers()) { return CODE_MANAGED_USERS_NOT_SUPPORTED; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index da49eb37ebec..412cf81eb5a5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -475,6 +475,9 @@ public final class SystemServer { ActivityThread activityThread = ActivityThread.systemMain(); mSystemContext = activityThread.getSystemContext(); mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); + + final Context systemUiContext = activityThread.getSystemUiContext(); + systemUiContext.setTheme(DEFAULT_SYSTEM_THEME); } /** diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java new file mode 100644 index 000000000000..54ecab3af542 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -0,0 +1,85 @@ +/* + * 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 static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.MediumTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; +import org.junit.Test; + +/** + * Tests for the {@link ActivityRecord} class. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests + */ +@MediumTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ActivityRecordTests extends ActivityTestsBase { + 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 ActivityRecord record = createActivity(service, testActivityComponent, task); + + record.setTask(null); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 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 ActivityRecord record = createActivity(service, testActivityComponent, task); + + task.removeActivity(record); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 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 ActivityRecord record = createActivity(service, testActivityComponent, task); + + testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @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 ActivityRecord record = createActivity(service, testActivityComponent, oldTask); + final TaskRecord newTask = createTask(service, testActivityComponent, testStack); + + record.reparent(newTask, 0, null /*reason*/); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java new file mode 100644 index 000000000000..c5cc2ff22abd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -0,0 +1,239 @@ +/* + * 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 static org.mockito.Mockito.mock; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.support.test.InstrumentationRegistry; +import com.android.server.AttributeCache; +import com.android.server.wm.AppWindowContainerController; +import com.android.server.wm.StackWindowController; + +import com.android.server.wm.WindowManagerService; +import com.android.server.wm.WindowTestUtils; +import org.junit.Before; +import org.mockito.MockitoAnnotations; + +/** + * A base class to handle common operations in activity related unit tests. + */ +public class ActivityTestsBase { + private final Context mContext = InstrumentationRegistry.getContext(); + private static boolean sLooperPrepared; + private Handler mHandler; + + // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must + // be called at before any tests. + private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + if (!sLooperPrepared) { + sLooperPrepared = true; + Looper.prepare(); + } + } + + protected ActivityManagerService createActivityManagerService() { + return new TestActivityManagerService(mContext); + } + + protected static TestActivityStack createActivityStack(ActivityManagerService service, + int stackId, int displayId, boolean onTop) { + if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { + final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor) + .createTestStack(stackId, onTop); + return stack; + } + + return null; + } + + protected static ActivityRecord createActivity(ActivityManagerService service, + ComponentName component, TaskRecord task) { + Intent intent = new Intent(); + intent.setComponent(component); + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + AttributeCache.init(service.mContext); + final ActivityRecord activity = new ActivityRecord(service, null /* caller */, + 0 /* launchedFromPid */, 0, null, intent, null, + aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, + 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, + service.mStackSupervisor, null /* container */, null /* options */, + null /* sourceRecord */); + activity.mWindowContainerController = mock(AppWindowContainerController.class); + + if (task != null) { + task.addActivityToTop(activity); + } + + return activity; + } + + protected static TaskRecord createTask(ActivityManagerService service, + ComponentName component, ActivityStack stack) { + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + + Intent intent = new Intent(); + intent.setComponent(component); + + final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, + null /*_taskDescription*/, null /*thumbnailInfo*/); + stack.addTask(task, true, "creating test task"); + task.setStack(stack); + task.createWindowContainer(true, true); + + return task; + } + + /** + * An {@link ActivityManagerService} subclass which provides a test + * {@link ActivityStackSupervisor}. + */ + protected static class TestActivityManagerService extends ActivityManagerService { + public TestActivityManagerService(Context context) { + super(context); + } + + @Override + protected ActivityStackSupervisor createStackSupervisor() { + return new TestActivityStackSupervisor(this, new Handler().getLooper()); + } + } + + /** + * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on + * setup not available in the test environment. Also specifies an injector for + */ + protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { + public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { + super(service, looper); + } + + // Invoked during {@link ActivityStack} creation. + @Override + void updateUIDsPresentOnDisplay() { + } + + public TestActivityStack createTestStack(int stackId, boolean onTop) { + final ActivityDisplay display = new ActivityDisplay(); + final TestActivityContainer container = + new TestActivityContainer(stackId, display, onTop); + return container.getStack(); + } + + private class TestActivityContainer extends ActivityContainer { + private TestActivityStack mStack; + TestActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) { + super(stackId, activityDisplay, onTop); + } + + @Override + protected void createStack(int stackId, boolean onTop) { + mStack = new TestActivityStack(this, null /*recentTasks*/, onTop); + } + + public TestActivityStack getStack() { + return mStack; + } + } + } + + /** + * 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> { + private int mOnActivityRemovedFromStackCount = 0; + private T mContainerController; + TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, + RecentTasks recentTasks, boolean onTop) { + super(activityContainer, recentTasks, onTop); + } + + @Override + void onActivityRemovedFromStack(ActivityRecord r) { + mOnActivityRemovedFromStackCount++; + super.onActivityRemovedFromStack(r); + } + + // Returns the number of times {@link #onActivityRemovedFromStack} has been called + public int onActivityRemovedFromStackInvocationCount() { + return mOnActivityRemovedFromStackCount; + } + + @Override + protected T createStackWindowController(int displayId, boolean onTop, + Rect outBounds) { + mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); + return mContainerController; + } + + @Override + T getWindowContainerController() { + return mContainerController; + } + } + + protected static class ActivityStackBuilder { + private boolean mOnTop = true; + private int mStackId = 0; + private int mDisplayId = 1; + + private final ActivityManagerService mService; + + public ActivityStackBuilder(ActivityManagerService ams) { + mService = ams; + } + + public ActivityStackBuilder setOnTop(boolean onTop) { + mOnTop = onTop; + return this; + } + + public ActivityStackBuilder setStackId(int id) { + mStackId = id; + return this; + } + + public ActivityStackBuilder setDisplayId(int id) { + mDisplayId = id; + return this; + } + + public TestActivityStack build() { + return createActivityStack(mService, mStackId, mDisplayId, mOnTop); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 0f51c49bfaba..d281e5aed3cc 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -36,7 +36,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; @@ -1276,7 +1275,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { try { final ByteArrayOutputStream out = new ByteArrayOutputStream(); final PrintWriter pw = new PrintWriter(out); - mService.dump(/* fd */ null, pw, args); + mService.dumpNoCheck(/* fd */ null, pw, args); pw.close(); return out.toString(); @@ -1345,23 +1344,20 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcut(String id) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } @Deprecated // Title was renamed to short label. protected ShortcutInfo makeShortcutWithTitle(String id, String title) { return makeShortcut( id, title, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) { return makeShortcut( id, shortLabel, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } /** @@ -1370,8 +1366,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) { final ShortcutInfo s = makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); s.setTimestamp(timestamp); return s; } @@ -1383,8 +1378,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { ComponentName activity) { final ShortcutInfo s = makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); s.setTimestamp(timestamp); return s; } @@ -1395,27 +1389,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, icon, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); - } - - protected ShortcutInfo makeChooserShortcut(String id, int i, boolean includeIntent) { - List<IntentFilter> filters = new ArrayList<>(); - List<ComponentName> componentNames = new ArrayList<>(); - for(int j = 0; j < i; j++) { - final IntentFilter filter = new IntentFilter(); - filter.addAction("view"); - filters.add(filter); - - componentNames.add(new ComponentName("xxxx", "yy" + i)); - } - Intent intent = null; - if (includeIntent) { - intent = makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class); - } - return makeShortcut( - id, "Title-" + id, /* activity =*/ null, /* icon */ null, - intent, /* rank =*/ 0, filters, componentNames); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makePackageShortcut(String packageName, String id) { @@ -1424,8 +1398,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { setCaller(packageName); ShortcutInfo s = makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); setCaller(origCaller); // restore the caller return s; @@ -1449,52 +1422,39 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithActivity(String id, ComponentName activity) { return makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithIntent(String id, Intent intent) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - intent, /* rank =*/ 0, /* chooserFilters =*/ null, - /* chooserComponentNames =*/ null); - + intent, /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithActivityAndTitle(String id, ComponentName activity, String title) { return makeShortcut( id, title, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, - /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); } protected ShortcutInfo makeShortcutWithActivityAndRank(String id, ComponentName activity, int rank) { return makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank, - /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank); } /** * Make a shortcut with details. */ protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity, - Icon icon, Intent intent, int rank, @Nullable List<IntentFilter> chooserFilters, - @Nullable List<ComponentName> chooserComponentNames) { + Icon icon, Intent intent, int rank) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) .setShortLabel(title) - .setRank(rank); - if (intent != null) { - b.setIntent(intent); - } - if (chooserFilters != null) { - for (int i = 0; i < chooserFilters.size(); i++) { - b.addChooserIntentFilter(chooserFilters.get(i), chooserComponentNames.get(i)); - } - } + .setRank(rank) + .setIntent(intent); if (icon != null) { b.setIcon(icon); } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index dd0871ae49e0..9861aa177135 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -17,7 +17,6 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyOrNull; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyStringOrNull; -import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllChooser; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned; @@ -58,7 +57,6 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -261,9 +259,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); final ShortcutInfo si2 = makeShortcut( "shortcut2", @@ -271,18 +267,14 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 12); final ShortcutInfo si3 = makeShortcut( "shortcut3", "Title 3", /* activity */ null, icon3, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 13, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 13); assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3))); assertShortcutIds(assertAllNotKeyFieldsOnly( @@ -993,10 +985,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"), - makeShortcut("s5"), - makeChooserShortcut("s6", 2, true), - makeChooserShortcut("s7", 2, true), - makeChooserShortcut("s8", 1, true)))); + makeShortcut("s5") + ))); }); runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -1004,13 +994,11 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"), - makeShortcut("s5"), - makeChooserShortcut("s6", 2, true), - makeChooserShortcut("s7", 2, true), - makeChooserShortcut("s8", 1, true)))); + makeShortcut("s5") + ))); }); runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3", "s6"), + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), getCallingUser()); mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"), getCallingUser()); @@ -1023,20 +1011,19 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.removeDynamicShortcuts(list("s1")); mManager.removeDynamicShortcuts(list("s3")); mManager.removeDynamicShortcuts(list("s5")); - mManager.removeDynamicShortcuts(list("s7")); }); runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s3", "s4", "s5", "s6", "s7", "s8"); + "s3", "s4", "s5"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), - "s2", "s3", "s6"); + "s2", "s3"); }); runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s2", "s4", "s6", "s8"); + "s2", "s4"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), "s4", "s5"); @@ -1073,10 +1060,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s3", "s4", "s5", "s6", "s7", "s8"); + "s3", "s4", "s5"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), - "s2", "s3", "s6"); + "s2", "s3"); ShortcutInfo s = getCallerShortcut("s2"); assertTrue(s.hasIconResource()); @@ -1092,7 +1079,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s2", "s4", "s6", "s8"); + "s2", "s4"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), "s4", "s5"); @@ -1189,46 +1176,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void testUpdateShortcuts_chooser() { - runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), - makeChooserShortcut("s2", 2, false), - makeChooserShortcut("s3", 2, false) - ))); - - assertFalse(getCallerShortcut("s1").isChooser()); - assertTrue(getCallerShortcut("s2").isChooser()); - assertTrue(getCallerShortcut("s3").isChooser()); - - ShortcutInfo s = getCallerShortcut("s1"); - assertNull(s.getChooserIntentFilters()); - assertNull(s.getChooserComponentNames()); - - assertTrue(getCallerShortcut("s1").isDynamic()); - assertFalse(getCallerShortcut("s2").isDynamic()); - assertFalse(getCallerShortcut("s3").isDynamic()); - - - // Replace 2 with a chooser shortcut - mManager.updateShortcuts(list(makeChooserShortcut("s1", 2, true))); - - s = getCallerShortcut("s1"); - assertEquals(2, s.getChooserIntentFilters().length); - assertEquals(2, s.getChooserComponentNames().length); - - assertShortcutIds(assertAllChooser( - mManager.getDynamicShortcuts()), - "s1", "s2", "s3"); - - assertTrue(getCallerShortcut("s1").isDynamic()); - assertFalse(getCallerShortcut("s2").isDynamic()); - assertFalse(getCallerShortcut("s3").isDynamic()); - }); - } - - - // === Test for launcher side APIs === + // === Test for launcher side APIs === public void testGetShortcuts() { @@ -1539,17 +1487,15 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); final ShortcutInfo s1_2 = makeShortcut( - "s2", "Title 2", + "s2", + "Title 2", /* activity */ null, /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 12); assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2))); dumpsysOnLogcat(); @@ -1562,9 +1508,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); assertTrue(mManager.setDynamicShortcuts(list(s2_1))); dumpsysOnLogcat(); @@ -2733,12 +2677,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { final ShortcutInfo s1_2 = makeShortcut( "s2", "Title 2", - /* activity */ null, - /* icon =*/ null, + /* activity */ null, + /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* rank */ 12, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* rank */ 12); final ShortcutInfo s1_3 = makeShortcut("s3"); @@ -2753,9 +2695,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); assertTrue(mManager.setDynamicShortcuts(list(s2_1))); }); @@ -3175,9 +3115,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3185,9 +3123,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12, - /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 12); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -3205,8 +3141,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeComponent(ShortcutActivity.class), icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, - "key1", "val1", "nest", makeBundle("key", 123)), /* weight */ 10, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + "key1", "val1", "nest", makeBundle("key", 123)), + /* weight */ 10); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3214,8 +3150,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12, /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 12); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -3237,8 +3172,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10, /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 10); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3246,8 +3180,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12, /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null); + /* weight */ 12); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -6884,12 +6817,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.setDynamicShortcuts(list( makeShortcut("ms1", "title1", new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - /* icon */ null, new Intent("action1"), /* rank */ 0, - /* chooserFilter=*/ null, /* chooserComponentNames=*/ null), + /* icon */ null, new Intent("action1"), /* rank */ 0), makeShortcut("ms2", "title2", new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - /* icon */ null, new Intent("action1"), /* rank */ 0, /* chooserFilter=*/ null, - /* chooserComponentNames=*/ null))); + /* icon */ null, new Intent("action1"), /* rank */ 0))); }); runWithCaller(LAUNCHER_1, USER_0, () -> { diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 7c5eb07f0a78..9880caa11f85 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -34,7 +34,6 @@ import android.Manifest.permission; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.BitmapFactory; @@ -93,6 +92,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertExpectException( RuntimeException.class, + "intents cannot contain null", + () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null)); + + assertExpectException( + RuntimeException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); @@ -137,19 +141,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { "disabledMessage cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage("")); - - assertExpectException( - RuntimeException.class, - "component name cannot be null", - () -> new ShortcutInfo.Builder(getTestContext(), "id") - .addChooserIntentFilter(new IntentFilter(Intent.ACTION_SEND), null)); - - assertExpectException( - RuntimeException.class, - "intent filter cannot be null", - () -> new ShortcutInfo.Builder(getTestContext(), "id") - .addChooserIntentFilter(null, new ComponentName("xxx", "s"))); - assertExpectException(NullPointerException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); @@ -248,10 +239,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); - IntentFilter chooserFilter = new IntentFilter(); - chooserFilter.addAction(Intent.ACTION_VIEW); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("l", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") @@ -264,8 +251,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) - .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) - .setChooserExtras(pb2) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -296,12 +281,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getTextResName()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); - - assertEquals(1, si.getChooserIntentFilters().length); - assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); - assertEquals(1, si.getChooserComponentNames().length); - assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); - assertEquals(1, si.getChooserExtras().getInt("l")); } public void testShortcutInfoParcel_resId() { @@ -310,10 +289,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); - IntentFilter chooserFilter = new IntentFilter(); - chooserFilter.addAction(Intent.ACTION_VIEW); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("l", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") @@ -326,8 +301,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) - .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) - .setChooserExtras(pb2) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -364,11 +337,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); - IntentFilter chooserFilter = new IntentFilter(); - chooserFilter.addAction(Intent.ACTION_VIEW); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("l", 1); - ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -380,8 +348,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) - .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) - .setChooserExtras(pb2) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); @@ -411,12 +377,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); - assertEquals(1, si.getChooserIntentFilters().length); - assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); - assertEquals(1, si.getChooserComponentNames().length); - assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); - assertEquals(1, si.getChooserExtras().getInt("l")); - si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); @@ -484,10 +444,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); - IntentFilter chooserFilter = new IntentFilter(); - chooserFilter.addAction(Intent.ACTION_VIEW); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("l", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -499,8 +455,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) - .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) - .setChooserExtras(pb2) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); @@ -533,12 +487,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); - assertEquals(1, si.getChooserIntentFilters().length); - assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); - assertEquals(1, si.getChooserComponentNames().length); - assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); - assertEquals(1, si.getChooserExtras().getInt("l")); - si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); @@ -654,10 +602,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); - IntentFilter chooserFilter = new IntentFilter(); - chooserFilter.addAction(Intent.ACTION_VIEW); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("l", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -769,12 +713,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(999, si.getRank()); - PersistableBundle pb3 = new PersistableBundle(); - pb3.putInt("x", 99); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("x", 99); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") - .setExtras(pb3).build()); + .setExtras(pb2).build()); assertEquals("text", si.getText()); assertEquals(99, si.getExtras().getInt("x")); } @@ -2096,16 +2040,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(expected, dumpCheckin()); } - public void testDumpsysNoPermission() { - assertExpectException(SecurityException.class, "android.permission.DUMP", - () -> mService.dump(null, new PrintWriter(new StringWriter()), null)); - - // System can call it without the permission. - runWithSystemUid(() -> { - mService.dump(null, new PrintWriter(new StringWriter()), null); - }); - } - /** * Make sure the legacy file format that only supported a single intent per shortcut * can still be read. diff --git a/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java b/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java new file mode 100644 index 000000000000..f68e06afff9a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java @@ -0,0 +1,144 @@ +/* + * 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.app.ActivityManager; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Debug; +import android.view.DisplayInfo; +import org.junit.Test; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test class to exercise logic related to {@link android.content.res.Configuration#appBounds}. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.wm.AppBoundsTests + */ +@SmallTest +@Presubmit +@org.junit.runner.RunWith(AndroidJUnit4.class) +public class AppBoundsTests extends WindowTestsBase { + private Rect mParentBounds; + + @Override + public void setUp() throws Exception { + super.setUp(); + mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); + } + + /** + * Ensures the configuration app bounds at the root level match the app dimensions. + */ + @Test + public void testRootConfigurationBounds() throws Exception { + final DisplayInfo info = sDisplayContent.getDisplayInfo(); + info.appWidth = 1024; + info.appHeight = 768; + + final Configuration config = sWm.computeNewConfiguration(sDisplayContent.getDisplayId()); + // The bounds should always be positioned in the top left. + assertEquals(config.appBounds.left, 0); + assertEquals(config.appBounds.top, 0); + + // The bounds should equal the defined app width and height + assertEquals(config.appBounds.width(), info.appWidth); + assertEquals(config.appBounds.height(), info.appHeight); + } + + /** + * Ensures that bounds are clipped to their parent. + */ + @Test + public void testBoundsClipping() throws Exception { + final Rect shiftedBounds = new Rect(mParentBounds); + shiftedBounds.offset(10, 10); + final Rect expectedBounds = new Rect(mParentBounds); + expectedBounds.intersect(shiftedBounds); + testStackBoundsConfiguration(null /*stackId*/, mParentBounds, shiftedBounds, + expectedBounds); + } + + /** + * Ensures that empty bounds are not propagated to the configuration. + */ + @Test + public void testEmptyBounds() throws Exception { + final Rect emptyBounds = new Rect(); + testStackBoundsConfiguration(null /*stackId*/, mParentBounds, emptyBounds, + null /*ExpectedBounds*/); + } + + /** + * Ensures that bounds on freeform stacks are not clipped. + */ + @Test + public void testFreeFormBounds() throws Exception { + final Rect freeFormBounds = new Rect(mParentBounds); + freeFormBounds.offset(10, 10); + testStackBoundsConfiguration(ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID, + mParentBounds, freeFormBounds, freeFormBounds); + } + + /** + * Ensures that fully contained bounds are not clipped. + */ + @Test + public void testContainedBounds() throws Exception { + final Rect insetBounds = new Rect(mParentBounds); + insetBounds.inset(5, 5, 5, 5); + testStackBoundsConfiguration(null /*stackId*/, mParentBounds, insetBounds, insetBounds); + } + + /** + * Ensures that full screen free form bounds are clipped + */ + @Test + public void testFullScreenFreeFormBounds() throws Exception { + final Rect fullScreenBounds = new Rect(0, 0, sDisplayInfo.logicalWidth, + sDisplayInfo.logicalHeight); + testStackBoundsConfiguration(null /*stackId*/, mParentBounds, fullScreenBounds, + mParentBounds); + } + + + private void testStackBoundsConfiguration(Integer stackId, Rect parentBounds, Rect bounds, + Rect expectedConfigBounds) { + final StackWindowController stackController = stackId != null ? + createStackControllerOnStackOnDisplay(stackId, sDisplayContent) + : createStackControllerOnDisplay(sDisplayContent); + + final Configuration parentConfig = sDisplayContent.getConfiguration(); + parentConfig.setAppBounds(parentBounds); + + final Configuration config = new Configuration(); + stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/, + new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/, + false /*overrideHeight*/, sDisplayInfo.logicalDensityDpi, config, parentConfig); + // Assert that both expected and actual are null or are equal to each other + + assertTrue((expectedConfigBounds == null && config.appBounds == null) + || expectedConfigBounds.equals(config.appBounds)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 2ccaefc4512e..25004de60676 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -44,7 +44,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert token was added to display. assertNotNull(sDisplayContent.getWindowToken(controller.mToken.asBinder())); @@ -61,7 +62,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testSetOrientation() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert orientation is unspecified to start. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); @@ -92,7 +94,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testCreateRemoveStartingWindow() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -105,8 +108,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindow() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -120,8 +125,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); sPolicy.setRunnableWhenAddingSplashScreen(() -> { // Surprise, ...! Transfer window in the middle of the creation flow. @@ -140,16 +147,16 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController1 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController1 = createAppWindowController( - taskController1); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController2 = createAppWindowController( - taskController2); - final TestTaskWindowContainerController taskController3 = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController1 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController1 = + createAppWindowController(taskController1); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController2 = + createAppWindowController(taskController2); + final WindowTestUtils.TestTaskWindowContainerController taskController3 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); try { appWindowController1.reparent(taskController1, 0); @@ -169,16 +176,18 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { // Reparent the app window and ensure that it is moved appWindowController1.reparent(taskController2, 0); assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); - assertEquals(0, ((TestAppWindowToken) appWindowController1.mContainer).positionInParent()); - assertEquals(1, ((TestAppWindowToken) appWindowController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer) + .positionInParent()); + assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer) + .positionInParent()); } - private TestAppWindowContainerController createAppWindowController() { - return createAppWindowController(new TestTaskWindowContainerController()); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController() { + return createAppWindowController(new WindowTestUtils.TestTaskWindowContainerController()); } - private TestAppWindowContainerController createAppWindowController( - TestTaskWindowContainerController taskController) { - return new TestAppWindowContainerController(taskController); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController( + WindowTestUtils.TestTaskWindowContainerController taskController) { + return new WindowTestUtils.TestAppWindowContainerController(taskController); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 2003b91bcfad..7a7ca3f4000a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testAddWindow_Order() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -78,7 +79,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testFindMainWindow() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertNull(token.findMainWindow()); @@ -102,12 +104,13 @@ public class AppWindowTokenTests extends WindowTestsBase { // Create an app window with token on a display. final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -137,12 +140,13 @@ public class AppWindowTokenTests extends WindowTestsBase { final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked(); final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(defaultDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(defaultDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -165,7 +169,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testGetOrientation() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 1729ceec0005..d7d365e72480 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -175,7 +175,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc, stack.getDisplayContent()); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); assertEquals(dc, task.getDisplayContent()); assertEquals(dc, token.getDisplayContent()); @@ -213,10 +213,10 @@ public class DisplayContentTests extends WindowTestsBase { */ @Test public void testDefaultDisplayOverrideConfigUpdate() throws Exception { - final Configuration currentOverrideConfig = sDisplayContent.getOverrideConfiguration(); + final Configuration currentConfig = sDisplayContent.getConfiguration(); // Create new, slightly changed override configuration and apply it to the display. - final Configuration newOverrideConfig = new Configuration(currentOverrideConfig); + final Configuration newOverrideConfig = new Configuration(currentConfig); newOverrideConfig.densityDpi += 120; newOverrideConfig.fontScale += 0.3; @@ -228,10 +228,10 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); // Return back to original values. - sWm.setNewDisplayOverrideConfiguration(currentOverrideConfig, DEFAULT_DISPLAY); + sWm.setNewDisplayOverrideConfiguration(currentConfig, DEFAULT_DISPLAY); globalConfig = sWm.mRoot.getConfiguration(); - assertEquals(currentOverrideConfig.densityDpi, globalConfig.densityDpi); - assertEquals(currentOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); + assertEquals(currentConfig.densityDpi, globalConfig.densityDpi); + assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); } @Test diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java index b0eba0b99567..13098f64bfac 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java @@ -16,12 +16,7 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,8 +43,8 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; final Task task = taskController.mContainer; @@ -68,11 +63,11 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer_deferRemoval() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; // Stack removal is deferred if one of its child is animating. task.setLocalIsAnimating(true); @@ -96,9 +91,9 @@ public class StackWindowControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; // Create second display and put second stack on it. diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index aab75ee1699b..717ddf26eb2f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -24,15 +27,19 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.Surface; + +import com.android.server.wm.TaskSnapshotSurface.Window; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,59 +55,174 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { private TaskSnapshotSurface mSurface; - @Before - public void setUp() { - mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE); + private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis, + int windowFlags, Rect taskBounds) { + final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER); + final TaskSnapshot snapshot = new TaskSnapshot(buffer, + ORIENTATION_PORTRAIT, contentInsets, false, 1.0f); + mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test", + Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds); + } + + private void setupSurface(int width, int height) { + setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, width, height)); } @Test public void fillEmptyBackground_fillHorizontally() throws Exception { + setupSurface(200, 100); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); } @Test public void fillEmptyBackground_fillVertically() throws Exception { + setupSurface(100, 200); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(200); - final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); } @Test public void fillEmptyBackground_fillBoth() throws Exception { + setupSurface(200, 200); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(200); - final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); } @Test public void fillEmptyBackground_dontFill_sameSize() throws Exception { + setupSurface(100, 100); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @Test public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception { + setupSurface(100, 100); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void testCalculateSnapshotCrop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_taskNotOnTop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarLeft() { + setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarRight() { + setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotFrame() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 0, 10); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 100, 70), + mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); + } + + @Test + public void testCalculateSnapshotFrame_navBarLeft() { + setupSurface(100, 100); + final Rect insets = new Rect(10, 10, 0, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 90, 80), + mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); + } + + @Test + public void testDrawStatusBarBackground() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10); + verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); + } + + @Test + public void testDrawStatusBarBackground_nope() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } + + @Test + public void testDrawNavigationBarBackground() { + final Rect insets = new Rect(0, 10, 0, 10); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_left() { + final Rect insets = new Rect(10, 10, 0, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_right() { + final Rect insets = new Rect(0, 10, 10, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java index 462bd68dc420..82ea2313e8d7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java @@ -56,7 +56,7 @@ public class TaskStackContainersTests extends WindowTestsBase { // Stack should contain visible app window to be considered visible. final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); assertFalse(mPinnedStack.isVisible()); - final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken pinnedApp = new WindowTestUtils.TestAppWindowToken(sDisplayContent); pinnedTask.addChild(pinnedApp, 0 /* addPos */); assertTrue(mPinnedStack.isVisible()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java index 9dbd8a617eec..267e5f77e709 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import android.content.pm.ActivityInfo; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import org.junit.Test; import org.junit.runner.RunWith; @@ -67,12 +64,14 @@ public class TaskStackTests extends WindowTestsBase { public void testClosingAppDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); @@ -85,12 +84,14 @@ public class TaskStackTests extends WindowTestsBase { public void testMoveTaskToBackDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java index f79908e906c7..1819c56735eb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -16,14 +16,9 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - -import android.hardware.display.DisplayManagerGlobal; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +40,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); taskController.removeContainer(); // Assert that the container was removed. @@ -58,12 +53,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; final AppWindowToken app = appController.mContainer; task.mShouldDeferRemoval = true; @@ -85,12 +80,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController1 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController1); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController1); final StackWindowController stackController2 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController2); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController2); boolean gotException = false; try { @@ -114,8 +109,8 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { taskController.reparent(stackController2, 0); assertEquals(stackController2.mContainer, taskController.mContainer.getParent()); - assertEquals(0, ((TestTask) taskController.mContainer).positionInParent()); - assertEquals(1, ((TestTask) taskController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent()); + assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent()); } @Test @@ -124,9 +119,9 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; assertEquals(sDisplayContent, stack1.getDisplayContent()); @@ -134,9 +129,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); final TaskStack stack2 = stack2Controller.mContainer; - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stack2Controller); - final TestTask task2 = (TestTask) taskController2.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stack2Controller); + final WindowTestUtils.TestTask task2 = + (WindowTestUtils.TestTask) taskController2.mContainer; // Reparent and check state taskController.reparent(stack2Controller, 0); 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 0b72a21ca30d..fbeda0a9191c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; +import android.content.pm.ActivityInfo; import android.os.PowerSaveState; import org.mockito.invocation.InvocationOnMock; @@ -449,9 +450,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public boolean rotationHasCompatibleMetricsLw(int orientation, - int rotation) { - return false; + public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) { + return true; } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index cf8af6716d34..0eaf5bc3e940 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -46,7 +46,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testFlow() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token); sWm.mUnknownAppVisibilityController.notifyRelayouted(token); @@ -58,8 +58,8 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testMultiple() throws Exception { - final AppWindowToken token1 = new TestAppWindowToken(sDisplayContent); - final AppWindowToken token2 = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token1 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); + final AppWindowToken token2 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token1); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); sWm.mUnknownAppVisibilityController.notifyLaunched(token2); @@ -74,7 +74,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testClear() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.clear();; assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); @@ -82,7 +82,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testAppRemoved() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.appRemoved(token); assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 1b1984dba8f4..a2aa058ac063 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -102,7 +102,7 @@ public class WindowFrameTests extends WindowTestsBase { // Just any non zero value. sWm.mSystemDecorLayer = 10000; - mWindowToken = new TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); + mWindowToken = new WindowTestUtils.TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); mStubStack = new TaskStack(sWm, 0); } @@ -250,37 +250,57 @@ public class WindowFrameTests extends WindowTestsBase { @Test public void testLayoutNonfullscreenTask() { - final Rect taskBounds = new Rect(300, 300, 700, 700); + final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo(); + final int logicalWidth = displayInfo.logicalWidth; + final int logicalHeight = displayInfo.logicalHeight; + + final int taskLeft = logicalWidth / 4; + final int taskTop = logicalHeight / 4; + final int taskRight = logicalWidth / 4 * 3; + final int taskBottom = logicalHeight / 4 * 3; + final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); TaskWithBounds task = new TaskWithBounds(taskBounds); task.mFullscreenForTest = false; WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - final Rect pf = new Rect(0, 0, 1000, 1000); + final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. - assertRect(w.mFrame, 300, 300, 700, 700); - assertRect(w.getContentFrameLw(), 300, 300, 700, 700); + assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom); + assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom); assertRect(w.mContentInsets, 0, 0, 0, 0); - pf.set(0, 0, 1000, 1000); + pf.set(0, 0, logicalWidth, logicalHeight); // We still produce insets against the containing frame the same way. - final Rect cf = new Rect(0, 0, 500, 500); + final int cfRight = logicalWidth / 2; + final int cfBottom = logicalHeight / 2; + final Rect cf = new Rect(0, 0, cfRight, cfBottom); w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null); - assertRect(w.mFrame, 300, 300, 700, 700); - assertRect(w.getContentFrameLw(), 300, 300, 500, 500); - assertRect(w.mContentInsets, 0, 0, 200, 200); - - pf.set(0, 0, 1000, 1000); + assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom); + int contentInsetRight = taskRight - cfRight; + int contentInsetBottom = taskBottom - cfBottom; + assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom); + assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight, + taskBottom - contentInsetBottom); + + pf.set(0, 0, logicalWidth, logicalHeight); // However if we set temp inset bounds, the insets will be computed // as if our window was laid out there, but it will be laid out according to // the task bounds. - task.mInsetBounds.set(200, 200, 600, 600); + final int insetLeft = logicalWidth / 5; + final int insetTop = logicalHeight / 5; + final int insetRight = insetLeft + (taskRight - taskLeft); + final int insetBottom = insetTop + (taskBottom - taskTop); + task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom); w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null); - assertRect(w.mFrame, 300, 300, 700, 700); - assertRect(w.getContentFrameLw(), 300, 300, 600, 600); - assertRect(w.mContentInsets, 0, 0, 100, 100); + assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom); + contentInsetRight = insetRight - cfRight; + contentInsetBottom = insetBottom - cfBottom; + assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom); + assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight, + taskBottom - contentInsetBottom); } @Test @@ -289,13 +309,16 @@ public class WindowFrameTests extends WindowTestsBase { new TaskWithBounds(null), FILL_PARENT, FILL_PARENT); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - final Rect pf = new Rect(0, 0, 1000, 1000); + final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); + final int logicalWidth = displayInfo.logicalWidth; + final int logicalHeight = displayInfo.logicalHeight; + final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final Rect df = pf; final Rect of = df; final Rect cf = new Rect(pf); // Produce some insets - cf.top += 50; - cf.bottom -= 100; + cf.top += displayInfo.logicalWidth / 10; + cf.bottom -= displayInfo.logicalWidth / 5; final Rect vf = cf; final Rect sf = vf; // We use a decor content frame with insets to produce cropping. @@ -308,35 +331,34 @@ public class WindowFrameTests extends WindowTestsBase { // If we were above system decor we wouldnt' get any cropping though w.mLayer = sWm.mSystemDecorLayer + 1; w.calculatePolicyCrop(policyCrop); - assertRect(policyCrop, 0, 0, 1000, 1000); + assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight); w.mLayer = 1; dcf.setEmpty(); // Likewise with no decor frame we would get no crop w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null); w.calculatePolicyCrop(policyCrop); - assertRect(policyCrop, 0, 0, 1000, 1000); + assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight); // Now we set up a window which doesn't fill the entire decor frame. // Normally it would be cropped to it's frame but in the case of docked resizing // we need to account for the fact the windows surface will be made // fullscreen and thus also make the crop fullscreen. w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - w.mAttrs.width = 500; - w.mAttrs.height = 500; - w.mRequestedWidth = 500; - w.mRequestedHeight = 500; + w.mAttrs.width = logicalWidth / 2; + w.mAttrs.height = logicalHeight / 2; + w.mRequestedWidth = logicalWidth / 2; + w.mRequestedHeight = logicalHeight / 2; w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf); w.calculatePolicyCrop(policyCrop); // Normally the crop is shrunk from the decor frame // to the computed window frame. - assertRect(policyCrop, 0, 0, 500, 500); + assertRect(policyCrop, 0, 0, logicalWidth / 2, logicalHeight / 2); w.mDockedResizingForTest = true; w.calculatePolicyCrop(policyCrop); // But if we are docked resizing it won't be, however we will still be // shrunk to the decor frame and the display. - final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); assertRect(policyCrop, 0, 0, Math.min(pf.width(), displayInfo.logicalWidth), Math.min(pf.height(), displayInfo.logicalHeight)); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java new file mode 100644 index 000000000000..3a443575332e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -0,0 +1,308 @@ +/* + * 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.app.ActivityManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.WindowManager; + +import static android.app.AppOpsManager.OP_NONE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.EMPTY; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static org.mockito.Mockito.mock; + +/** + * A collection of static functions that can be referenced by other test packages to provide access + * to WindowManager related test functionality. + */ +public class WindowTestUtils { + public static int sNextTaskId = 0; + + /** + * Retrieves an instance of {@link WindowManagerService}, creating it if necessary. + */ + public static WindowManagerService getWindowManagerService(Context context) { + return TestWindowManagerPolicy.getWindowManagerService(context); + } + + /** + * Creates a mock instance of {@link StackWindowController}. + */ + public static StackWindowController createMockStackWindowContainerController() { + StackWindowController controller = mock(StackWindowController.class); + controller.mContainer = mock(TestTaskStack.class); + return controller; + } + + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + int userId) { + final Task newTask = new Task(WindowTestUtils.sNextTaskId++, stack, userId, service, null, + EMPTY, 0, false, + false, new ActivityManager.TaskDescription(), null); + stack.addTask(newTask, POSITION_TOP); + return newTask; + } + + /** + * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not + * normally be mocked out. + */ + public static class TestTaskStack extends TaskStack { + TestTaskStack(WindowManagerService service, int stackId) { + super(service, stackId); + } + + @Override + void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { + // Do nothing. + } + } + + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ + public static class TestAppWindowToken extends AppWindowToken { + + TestAppWindowToken(DisplayContent dc) { + super(WindowTestsBase.sWm, null, false, dc, true /* fillsParent */, + null /* overrideConfig */, null /* bounds */); + } + + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, + showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, + launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + WindowState getFirstChild() { + return mChildren.getFirst(); + } + + WindowState getLastChild() { + return mChildren.getLast(); + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + } + + /* Used so we can gain access to some protected members of the {@link WindowToken} class */ + public static class TestWindowToken extends WindowToken { + int adj = 0; + + TestWindowToken(int type, DisplayContent dc) { + this(type, dc, false /* persistOnEmpty */); + } + + TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { + super(WindowTestsBase.sWm, mock(IBinder.class), type, persistOnEmpty, dc, + false /* ownerCanManageAppTokens */); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + @Override + int getAnimLayerAdjustment() { + return adj; + } + } + + /* Used so we can gain access to some protected members of the {@link Task} class */ + public static class TestTask extends Task { + boolean mShouldDeferRemoval = false; + boolean mOnDisplayChangedCalled = false; + private boolean mUseLocalIsAnimating = false; + private boolean mIsAnimating = false; + + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, TaskWindowContainerController controller) { + super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, new ActivityManager.TaskDescription(), + controller); + } + + boolean shouldDeferRemoval() { + return mShouldDeferRemoval; + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + @Override + void onDisplayChanged(DisplayContent dc) { + super.onDisplayChanged(dc); + mOnDisplayChangedCalled = true; + } + + @Override + boolean isAnimating() { + return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); + } + + void setLocalIsAnimating(boolean isAnimating) { + mUseLocalIsAnimating = true; + mIsAnimating = isAnimating; + } + } + + /** + * Used so we can gain access to some protected members of {@link TaskWindowContainerController} + * class. + */ + public static class TestTaskWindowContainerController extends TaskWindowContainerController { + + TestTaskWindowContainerController() { + this(WindowTestsBase.createStackControllerOnDisplay(WindowTestsBase.sDisplayContent)); + } + + TestTaskWindowContainerController(StackWindowController stackController) { + super(sNextTaskId++, new TaskWindowContainerListener() { + @Override + public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { + + } + + @Override + public void requestResize(Rect bounds, int resizeMode) { + + } + }, stackController, 0 /* userId */, null /* bounds */, + EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, + false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, + true /* showForAllUsers */, new ActivityManager.TaskDescription(), WindowTestsBase.sWm); + } + + @Override + TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, ActivityManager.TaskDescription taskDescription) { + return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, this); + } + } + + public static class TestAppWindowContainerController extends AppWindowContainerController { + + final IApplicationToken mToken; + + TestAppWindowContainerController(TestTaskWindowContainerController taskController) { + this(taskController, new TestIApplicationToken()); + } + + TestAppWindowContainerController(TestTaskWindowContainerController taskController, + IApplicationToken token) { + super(taskController, token, null /* listener */, 0 /* index */, + SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, + true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, + false /* launchTaskBehind */, false /* alwaysFocusable */, + 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, + 0 /* inputDispatchingTimeoutNanos */, WindowTestsBase.sWm, null /* overrideConfig */, + null /* bounds */); + mToken = token; + } + + @Override + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + return new TestAppWindowToken(service, token, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + controller, overrideConfig, bounds); + } + + AppWindowToken getAppWindowToken() { + return (AppWindowToken) WindowTestsBase.sDisplayContent.getWindowToken(mToken.asBinder()); + } + } + + public static class TestIApplicationToken implements IApplicationToken { + + private final Binder mBinder = new Binder(); + @Override + public IBinder asBinder() { + return mBinder; + } + } + + /** Used to track resize reports. */ + public static class TestWindowState extends WindowState { + boolean resizeReported; + + TestWindowState(WindowManagerService service, Session session, IWindow window, + WindowManager.LayoutParams attrs, WindowToken token) { + super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + } + + @Override + void reportResized() { + super.reportResized(); + resizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index a9d930f5c893..eaf4ac4baf4f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -19,21 +19,16 @@ package com.android.server.wm; import static android.view.View.VISIBLE; import android.app.ActivityManager.TaskDescription; -import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; -import android.os.Binder; import android.view.Display; import android.view.DisplayInfo; -import android.view.IApplicationToken; import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; -import android.app.ActivityManager.TaskSnapshot; import android.content.Context; -import android.os.IBinder; import android.support.test.InstrumentationRegistry; import android.view.IWindow; import android.view.WindowManager; @@ -41,10 +36,6 @@ import android.view.WindowManager; import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -58,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.mockito.Mockito.mock; import com.android.server.AttributeCache; @@ -78,8 +68,7 @@ class WindowTestsBase { // make sure we don't collide with any existing display. If we run into no other display, the // added display should be treated as default. This cannot be the default display private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1; - static int sNextStackId = FIRST_DYNAMIC_STACK_ID; - private static int sNextTaskId = 0; + private static int sNextStackId = FIRST_DYNAMIC_STACK_ID; private static boolean sOneTimeSetupDone = false; static DisplayContent sDisplayContent; @@ -184,14 +173,14 @@ class WindowTestsBase { private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) { if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { - return new TestWindowToken(type, dc); + return new WindowTestUtils.TestWindowToken(type, dc); } final TaskStack stack = stackId == INVALID_STACK_ID ? createTaskStackOnDisplay(dc) : createStackControllerOnStackOnDisplay(stackId, dc).mContainer; final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); return token; } @@ -209,7 +198,7 @@ class WindowTestsBase { } WindowState createAppWindow(Task task, int type, String name) { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(token, 0); return createWindow(null, type, token, name); } @@ -260,10 +249,7 @@ class WindowTestsBase { /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ static Task createTaskInStack(TaskStack stack, int userId) { - final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, 0, false, - false, new TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; + return WindowTestUtils.createTaskInStack(sWm, stack, userId); } /** Creates a {@link DisplayContent} and adds it to the system. */ @@ -274,227 +260,10 @@ class WindowTestsBase { return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm)); } - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - static class TestWindowToken extends WindowToken { - int adj = 0; - - TestWindowToken(int type, DisplayContent dc) { - this(type, dc, false /* persistOnEmpty */); - } - - TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { - super(sWm, mock(IBinder.class), type, persistOnEmpty, dc, - false /* ownerCanManageAppTokens */); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - @Override - int getAnimLayerAdjustment() { - return adj; - } - } - - /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - static class TestAppWindowToken extends AppWindowToken { - - TestAppWindowToken(DisplayContent dc) { - super(sWm, null, false, dc, true /* fillsParent */, null /* overrideConfig */, - null /* bounds */); - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, - showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, - launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - WindowState getFirstChild() { - return mChildren.getFirst(); - } - - WindowState getLastChild() { - return mChildren.getLast(); - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - } - - /* Used so we can gain access to some protected members of the {@link Task} class */ - class TestTask extends Task { - - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mUseLocalIsAnimating = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskWindowContainerController controller) { - super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, new TaskDescription(), controller); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isAnimating() { - return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); - } - - void setLocalIsAnimating(boolean isAnimating) { - mUseLocalIsAnimating = true; - mIsAnimating = isAnimating; - } - } - - /** - * Used so we can gain access to some protected members of {@link TaskWindowContainerController} - * class. - */ - class TestTaskWindowContainerController extends TaskWindowContainerController { - - TestTaskWindowContainerController() { - this(createStackControllerOnDisplay(sDisplayContent)); - } - - TestTaskWindowContainerController(StackWindowController stackController) { - super(sNextTaskId++, new TaskWindowContainerListener() { - @Override - public void onSnapshotChanged(TaskSnapshot snapshot) { - - } - - @Override - public void requestResize(Rect bounds, int resizeMode) { - - } - }, stackController, 0 /* userId */, null /* bounds */, - EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, - false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, - true /* showForAllUsers */, new TaskDescription(), sWm); - } - - @Override - TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, this); - } - } - - class TestAppWindowContainerController extends AppWindowContainerController { - - final IApplicationToken mToken; - - TestAppWindowContainerController(TestTaskWindowContainerController taskController) { - this(taskController, new TestIApplicationToken()); - } - - TestAppWindowContainerController(TestTaskWindowContainerController taskController, - IApplicationToken token) { - super(taskController, token, null /* listener */, 0 /* index */, - SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, - true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, - false /* launchTaskBehind */, false /* alwaysFocusable */, - 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, - 0 /* inputDispatchingTimeoutNanos */, sWm, null /* overrideConfig */, - null /* bounds */); - mToken = token; - } - - @Override - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - return new TestAppWindowToken(service, token, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller, overrideConfig, bounds); - } - - AppWindowToken getAppWindowToken() { - return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); - } + /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ + WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, + WindowToken token) { + return new WindowTestUtils.TestWindowState(sWm, sMockSession, sIWindow, attrs, token); } - class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - } - - /** Used to track resize reports. */ - class TestWindowState extends WindowState { - boolean resizeReported; - - TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { - super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - void reportResized() { - super.reportResized(); - resizeReported = true; - } - - @Override - public boolean isGoneForLayoutLw() { - return false; - } - - @Override - void updateResizingWindowIfNeeded() { - // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive - // the system that it can actually update the window. - boolean hadSurface = mHasSurface; - mHasSurface = true; - - super.updateResizingWindowIfNeeded(); - - mHasSurface = hadSurface; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java index babb6d9db31f..4f7ad41050f1 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -46,7 +46,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAddWindow() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -76,7 +77,7 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testChildRemoval() throws Exception { final DisplayContent dc = sDisplayContent; - final TestWindowToken token = new TestWindowToken(0, dc); + final WindowTestUtils.TestWindowToken token = new WindowTestUtils.TestWindowToken(0, dc); assertEquals(token, dc.getWindowToken(token.token)); @@ -95,7 +96,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAdjustAnimLayer() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); @@ -135,8 +137,9 @@ public class WindowTokenTests extends WindowTestsBase { */ @Test public void testTokenRemovalProcess() throws Exception { - final TestWindowToken token = - new TestWindowToken(TYPE_TOAST, sDisplayContent, true /* persistOnEmpty */); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(TYPE_TOAST, sDisplayContent, + true /* persistOnEmpty */); // Verify that the token is on the display assertNotNull(sDisplayContent.getWindowToken(token.token)); diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index c34293347814..922f08da643f 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -512,13 +512,6 @@ public class ShortcutManagerTestUtils { return actualShortcuts; } - public static List<ShortcutInfo> assertAllChooser(List<ShortcutInfo> actualShortcuts) { - for (ShortcutInfo s : actualShortcuts) { - assertTrue("ID " + s.getId(), s.isChooser()); - } - return actualShortcuts; - } - public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) { for (ShortcutInfo s : actualShortcuts) { assertTrue("ID " + s.getId(), s.isPinned()); diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index 7b8ebd44e629..01e36f57d1be 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -1039,18 +1039,34 @@ class UsbProfileGroupSettingsManager { * Start the appropriate package when an device/accessory got attached. * * @param intent The intent to start the package - * @param matches The available resolutions of the intent + * @param rawMatches The available resolutions of the intent * @param defaultActivity The default activity for the device (if set) * @param device The device if a device was attached * @param accessory The accessory if a device was attached */ - private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, + private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> rawMatches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory) { - int count = matches.size(); + final int numRawMatches = rawMatches.size(); + + // The raw matches contain the activities that can be started but also the intents to switch + // between the profiles + int numParentActivityMatches = 0; + int numNonParentActivityMatches = 0; + for (int i = 0; i < numRawMatches; i++) { + final ResolveInfo rawMatch = rawMatches.get(i); + if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { + if (UserHandle.getUserHandleForUid( + rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) { + numParentActivityMatches++; + } else { + numNonParentActivityMatches++; + } + } + } // don't show the resolver activity if there are no choices available - if (count == 0) { + if (numParentActivityMatches + numNonParentActivityMatches == 0) { if (accessory != null) { String uri = accessory.getUri(); if (uri != null && uri.length() > 0) { @@ -1073,6 +1089,21 @@ class UsbProfileGroupSettingsManager { return; } + // If only one profile has activity matches, we need to remove all switch intents + ArrayList<ResolveInfo> matches; + if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) { + matches = new ArrayList<>(numParentActivityMatches + numNonParentActivityMatches); + + for (int i = 0; i < numRawMatches; i++) { + ResolveInfo rawMatch = rawMatches.get(i); + if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { + matches.add(rawMatch); + } + } + } else { + matches = rawMatches; + } + if (defaultActivity != null) { UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser( UserHandle.getUserId(defaultActivity.applicationInfo.uid)); @@ -1101,7 +1132,7 @@ class UsbProfileGroupSettingsManager { resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); UserHandle user; - if (count == 1) { + if (matches.size() == 1) { ResolveInfo rInfo = matches.get(0); // start UsbConfirmActivity if there is only one choice diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 201f3ad5dce7..81f660047c6b 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1178,6 +1178,7 @@ public class SubscriptionManager { public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) { if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + intent.putExtra(Intent.EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); //FIXME this is using phoneId and slotIndex interchangeably //Eventually, this should be removed as it is not the slot id diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 26c9430408e8..8357a2b535a4 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -877,19 +877,16 @@ public class TelephonyManager { /** * USSD return code success. - * @hide */ public static final int USSD_RETURN_SUCCESS = 100; /** * USSD return code for failure case. - * @hide */ public static final int USSD_RETURN_FAILURE = -1; /** * USSD return code for failure case. - * @hide */ public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; @@ -2395,7 +2392,7 @@ public class TelephonyManager { * OR * {@link android.Manifest.permission#READ_SMS} * OR - * {@link android.Manifest.permission#READ_PHONE_NUMBER} + * {@link android.Manifest.permission#READ_PHONE_NUMBERS} * <p> * The default SMS app can also use this. */ @@ -2412,7 +2409,7 @@ public class TelephonyManager { * OR * {@link android.Manifest.permission#READ_SMS} * OR - * {@link android.Manifest.permission#READ_PHONE_NUMBER} + * {@link android.Manifest.permission#READ_PHONE_NUMBERS} * <p> * The default SMS app can also use this. * @@ -5113,16 +5110,32 @@ public class TelephonyManager { return new int[0]; } - public static abstract class OnReceiveUssdResponseCallback { + /* The caller of {@link #sendUssdRequest(String, UssdResponseCallback, Handler} provides + * once the network returns a USSD message or if there is failure. + * Either {@link #onReceiveUssdResponse(TelephonyManager, String, CharSequence} or + * {@link #onReceiveUssdResponseFailed(TelephonyManager, String, int} will be called. + */ + public static abstract class UssdResponseCallback { /** - ** Called when USSD has succeeded. + * Called when USSD has succeeded. The calling app can choose to either display the message + * or interpret the message. + * @param telephonyManager the TelephonyManager the callback is registered to. + * @param request the ussd code sent to the network. + * @param response the response from the network. **/ - public void onReceiveUssdResponse(String request, CharSequence response) {}; + public void onReceiveUssdResponse(final TelephonyManager telephonyManager, + String request, CharSequence response) {}; /** - ** Called when USSD has failed. + * Called when USSD has failed. + * @param telephonyManager the TelephonyManager the callback is registered to + * @param request the ussd code. + * @param failureCode failure code, should be either of + * {@link TelephonyManager#USSD_RETURN_FAILURE} or + * {@link TelephonyManager#USSD_ERROR_SERVICE_UNAVAIL}. **/ - public void onReceiveUssdResponseFailed(String request, int failureCode) {}; + public void onReceiveUssdResponseFailed(final TelephonyManager telephonyManager, + String request, int failureCode) {}; } /** @@ -5134,13 +5147,14 @@ public class TelephonyManager { * {@link android.Manifest.permission#CALL_PHONE} * @param ussdRequest the USSD command to be executed. * @param callback called by the framework to inform the caller of the result of executing the - * USSD request (see {@link OnReceiveUssdResponseCallback}). + * USSD request (see {@link UssdResponseCallback}). * @param handler the {@link Handler} to run the request on. */ @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String ussdRequest, - final OnReceiveUssdResponseCallback callback, Handler handler) { - checkNotNull(callback, "OnReceiveUssdResponseCallback cannot be null."); + final UssdResponseCallback callback, Handler handler) { + checkNotNull(callback, "UssdResponseCallback cannot be null."); + final TelephonyManager telephonyManager = this; ResultReceiver wrappedCallback = new ResultReceiver(handler) { @Override @@ -5150,10 +5164,11 @@ public class TelephonyManager { UssdResponse response = ussdResponse.getParcelable(USSD_RESPONSE); if (resultCode == USSD_RETURN_SUCCESS) { - callback.onReceiveUssdResponse(response.getUssdRequest(), + callback.onReceiveUssdResponse(telephonyManager, response.getUssdRequest(), response.getReturnMessage()); } else { - callback.onReceiveUssdResponseFailed(response.getUssdRequest(), resultCode); + callback.onReceiveUssdResponseFailed(telephonyManager, + response.getUssdRequest(), resultCode); } } }; @@ -5172,11 +5187,13 @@ public class TelephonyManager { } } - /* - * @return true, if the device is currently on a technology (e.g. UMTS or LTE) which can support - * voice and data simultaneously. This can change based on location or network condition. - */ - public boolean isConcurrentVoiceAndDataAllowed() { + /** + * Whether the device is currently on a technology (e.g. UMTS or LTE) which can support + * voice and data simultaneously. This can change based on location or network condition. + * + * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise. + */ + public boolean isConcurrentVoiceAndDataSupported() { try { ITelephony telephony = getITelephony(); return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId)); diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 73ee25ad5bcf..b4c531e99a95 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -334,9 +334,11 @@ public class TelephonyIntents { * <ul> * <li><em>subscription</em> - A int, the current default subscription.</li> * </ul> + * @deprecated Use {@link Intent#ACTION_DEFAULT_SUBSCRIPTION_CHANGED} */ + @Deprecated public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED - = "android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED"; + = Intent.ACTION_DEFAULT_SUBSCRIPTION_CHANGED; /** * Broadcast Action: The default data subscription has changed. This has the following @@ -364,9 +366,11 @@ public class TelephonyIntents { * <ul> * <li><em>subscription</em> - A int, the current sms default subscription.</li> * </ul> + * @deprecated Use {@link Intent#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} */ + @Deprecated public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED - = "android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + = Intent.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED; /* * Broadcast Action: An attempt to set phone radio type and access technology has changed. diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 04443a53527c..c562cb95ee31 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -80,6 +80,7 @@ import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult; import com.android.server.net.NetworkPinner; +import com.android.server.net.NetworkPolicyManagerInternal; import java.net.InetAddress; import java.util.ArrayList; @@ -713,6 +714,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { } mServiceContext = new MockContext(getContext()); + LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); + LocalServices.addService( + NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); mService = new WrappedConnectivityService(mServiceContext, mock(INetworkManagementService.class), mock(INetworkStatsService.class), diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java index 00dddeed3e8c..8739b7fade9a 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java @@ -311,7 +311,6 @@ public class RenderTestBase { sFrameworkRepo = null; sProjectResources = null; sLogger = null; - sBridge.dispose(); sBridge = null; TestUtils.gc(); @@ -329,7 +328,6 @@ public class RenderTestBase { RenderSession session = sBridge.createSession(params); try { - if (frameTimeNanos != -1) { session.setElapsedFrameTimeNanos(frameTimeNanos); } @@ -338,11 +336,13 @@ public class RenderTestBase { getLogger().error(session.getResult().getException(), session.getResult().getErrorMessage()); } - // Render the session with a timeout of 50s. - Result renderResult = session.render(50000); - if (!renderResult.isSuccess()) { - getLogger().error(session.getResult().getException(), - session.getResult().getErrorMessage()); + else { + // Render the session with a timeout of 50s. + Result renderResult = session.render(50000); + if (!renderResult.isSuccess()) { + getLogger().error(session.getResult().getException(), + session.getResult().getErrorMessage()); + } } return RenderResult.getFromSession(session); |