diff options
215 files changed, 4003 insertions, 2163 deletions
diff --git a/api/current.txt b/api/current.txt index 5361d0a977b1..5c0628c40d64 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28,7 +28,6 @@ package android { field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; - field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; @@ -34369,6 +34368,9 @@ package android.service.media { ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle); method public android.os.Bundle getExtras(); method public java.lang.String getRootId(); + field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; + field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT"; + field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; } public class MediaBrowserService.Result { @@ -34422,39 +34424,6 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { - ctor public NotificationAssistantService(); - method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); - method public final void clearAnnotation(java.lang.String); - method public void onNotificationActionClick(java.lang.String, long, int); - method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); - method public void onNotificationRemoved(java.lang.String, long, int); - method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); - method public final void setAnnotation(java.lang.String, android.app.Notification); - field public static final int REASON_APP_CANCEL = 8; // 0x8 - field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 - field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 - field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 - field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 - field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 - field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd - field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc - field public static final int REASON_LISTENER_CANCEL = 10; // 0xa - field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb - field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 - field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 - field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf - field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10 - field public static final int REASON_TOPIC_BANNED = 14; // 0xe - field public static final int REASON_USER_STOPPED = 6; // 0x6 - field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; - } - - public class NotificationAssistantService.Adjustment { - ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri); - } - public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -36475,6 +36444,7 @@ package android.telephony { field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; + field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool"; field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool"; @@ -40070,7 +40040,6 @@ package android.util { method public static android.util.LocaleList getDefault(); method public static android.util.LocaleList getEmptyLocaleList(); method public java.util.Locale getFirstMatch(java.lang.String[]); - method public java.util.Locale getPrimary(); method public int indexOf(java.util.Locale); method public boolean isEmpty(); method public static void setDefault(android.util.LocaleList); diff --git a/api/system-current.txt b/api/system-current.txt index a43e3f61c743..2adb1e61afdf 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -41,7 +41,6 @@ package android { field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; - field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; @@ -36779,6 +36778,9 @@ package android.service.media { ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle); method public android.os.Bundle getExtras(); method public java.lang.String getRootId(); + field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; + field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT"; + field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; } public class MediaBrowserService.Result { @@ -36836,13 +36838,11 @@ package android.service.notification { public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { ctor public NotificationAssistantService(); method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); - method public final void clearAnnotation(java.lang.String); method public void onNotificationActionClick(java.lang.String, long, int); method public void onNotificationClick(java.lang.String, long); method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); method public void onNotificationRemoved(java.lang.String, long, int); method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); - method public final void setAnnotation(java.lang.String, android.app.Notification); field public static final int REASON_APP_CANCEL = 8; // 0x8 field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 @@ -39059,6 +39059,7 @@ package android.telephony { field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; + field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool"; field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool"; @@ -42718,7 +42719,6 @@ package android.util { method public static android.util.LocaleList getDefault(); method public static android.util.LocaleList getEmptyLocaleList(); method public java.util.Locale getFirstMatch(java.lang.String[]); - method public java.util.Locale getPrimary(); method public int indexOf(java.util.Locale); method public boolean isEmpty(); method public static void setDefault(android.util.LocaleList); diff --git a/api/test-current.txt b/api/test-current.txt index 278a47d1debb..7abc6d43cd90 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -28,7 +28,6 @@ package android { field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; - field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; @@ -34384,6 +34383,9 @@ package android.service.media { ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle); method public android.os.Bundle getExtras(); method public java.lang.String getRootId(); + field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; + field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT"; + field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; } public class MediaBrowserService.Result { @@ -34437,39 +34439,6 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { - ctor public NotificationAssistantService(); - method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); - method public final void clearAnnotation(java.lang.String); - method public void onNotificationActionClick(java.lang.String, long, int); - method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); - method public void onNotificationRemoved(java.lang.String, long, int); - method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); - method public final void setAnnotation(java.lang.String, android.app.Notification); - field public static final int REASON_APP_CANCEL = 8; // 0x8 - field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 - field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 - field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 - field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 - field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 - field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd - field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc - field public static final int REASON_LISTENER_CANCEL = 10; // 0xa - field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb - field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 - field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 - field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf - field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10 - field public static final int REASON_TOPIC_BANNED = 14; // 0xe - field public static final int REASON_USER_STOPPED = 6; // 0x6 - field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; - } - - public class NotificationAssistantService.Adjustment { - ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri); - } - public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -36490,6 +36459,7 @@ package android.telephony { field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; + field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool"; field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool"; @@ -40087,7 +40057,6 @@ package android.util { method public static android.util.LocaleList getDefault(); method public static android.util.LocaleList getEmptyLocaleList(); method public java.util.Locale getFirstMatch(java.lang.String[]); - method public java.util.Locale getPrimary(); method public int indexOf(java.util.Locale); method public boolean isEmpty(); method public static void setDefault(android.util.LocaleList); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 805b2034ff05..6424520df3ec 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4822,14 +4822,12 @@ public final class ActivityThread { } private void updateDefaultDensity() { - if (mCurDefaultDisplayDpi != Configuration.DENSITY_DPI_UNDEFINED - && mCurDefaultDisplayDpi != DisplayMetrics.DENSITY_DEVICE - && !mDensityCompatMode) { - Slog.i(TAG, "Switching default density from " - + DisplayMetrics.DENSITY_DEVICE + " to " - + mCurDefaultDisplayDpi); - DisplayMetrics.DENSITY_DEVICE = mCurDefaultDisplayDpi; - Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); + final int densityDpi = mCurDefaultDisplayDpi; + if (!mDensityCompatMode + && densityDpi != Configuration.DENSITY_DPI_UNDEFINED + && densityDpi != DisplayMetrics.DENSITY_DEVICE) { + DisplayMetrics.DENSITY_DEVICE = densityDpi; + Bitmap.setDefaultDensity(densityDpi); } } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 1e22bef63d8c..da52c1e5ba1d 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -382,6 +382,13 @@ public final class LoadedApk { libraryPermittedPath += File.pathSeparator + System.getProperty("java.library.path"); } + // DO NOT SHIP: this is a workaround for apps loading native libraries + // provided by 3rd party apps using absolute path instead of corresponding + // classloader; see http://b/26954419 for example. + if (mApplicationInfo.targetSdkVersion <= 23) { + libraryPermittedPath += File.pathSeparator + "/data/app"; + } + // ----------------------------------------------------------------------------- final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 34c90c165490..a78076bdfa6f 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3236,6 +3236,7 @@ public class Notification implements Parcelable return; } contentView.setTextViewText(R.id.app_name_text, appName); + contentView.setTextColor(R.id.app_name_text, resolveColor()); } private void bindSmallIcon(RemoteViews contentView) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 1247afefcd01..02eb115175fd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4109,6 +4109,29 @@ public class DevicePolicyManager { } /** + * Called by the system to check if a specific accessibility service is disabled by admin. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName Accessibility service package name that needs to be checked. + * @param userHandle user id the admin is running as. + * @return true if the accessibility service is permitted, otherwise false. + * + * @hide + */ + public boolean isAccessibilityServicePermittedByAdmin(@NonNull ComponentName admin, + @NonNull String packageName, int userHandle) { + if (mService != null) { + try { + return mService.isAccessibilityServicePermittedByAdmin(admin, packageName, + userHandle); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + /** * Returns the list of accessibility services permitted by the device or profiles * owners of this user. * @@ -4188,6 +4211,28 @@ public class DevicePolicyManager { } /** + * Called by the system to check if a specific input method is disabled by admin. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName Input method package name that needs to be checked. + * @param userHandle user id the admin is running as. + * @return true if the input method is permitted, otherwise false. + * + * @hide + */ + public boolean isInputMethodPermittedByAdmin(@NonNull ComponentName admin, + @NonNull String packageName, int userHandle) { + if (mService != null) { + try { + return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + /** * Returns the list of input methods permitted by the device or profiles * owners of the current user. (*Not* calling user, due to a limitation in InputMethodManager.) * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index b57e1b7d8081..c6a53443b51c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -174,10 +174,12 @@ interface IDevicePolicyManager { boolean setPermittedAccessibilityServices(in ComponentName admin,in List packageList); List getPermittedAccessibilityServices(in ComponentName admin); List getPermittedAccessibilityServicesForUser(int userId); + boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId); boolean setPermittedInputMethods(in ComponentName admin,in List packageList); List getPermittedInputMethods(in ComponentName admin); List getPermittedInputMethodsForCurrentUser(); + boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId); boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden); boolean isApplicationHidden(in ComponentName admin, in String packageName); diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index b2ca023a017f..5398e7f2dc95 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -24,6 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.util.Log; +import static android.util.TimeUtils.formatForLogging; import java.util.ArrayList; @@ -640,12 +641,14 @@ public class JobInfo implements Parcelable { } JobInfo job = new JobInfo(this); if (job.intervalMillis != job.getIntervalMillis()) { - Log.w(TAG, "Specified interval is less than minimum interval. Clamped to " - + job.getIntervalMillis()); + Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is " + + formatForLogging(mIntervalMillis) + ". Clamped to " + + formatForLogging(job.getIntervalMillis())); } if (job.flexMillis != job.getFlexMillis()) { - Log.w(TAG, "Specified flex is less than minimum flex. Clamped to " - + job.getFlexMillis()); + Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is " + + formatForLogging(mFlexMillis) + ". Clamped to " + + formatForLogging(job.getFlexMillis())); } return job; } diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index a88aa312550d..2937ccc879a5 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -47,20 +47,6 @@ public final class UsageStats implements Parcelable { public long mLastTimeUsed; /** - * The last time the package was used via implicit, non-user initiated actions (service - * was bound, etc). - * {@hide} - */ - public long mLastTimeSystemUsed; - - /** - * Last time the package was used and the beginning of the idle countdown. - * This uses a different timebase that is about how much the device has been in use in general. - * {@hide} - */ - public long mBeginIdleTime; - - /** * {@hide} */ public long mTotalTimeInForeground; @@ -89,8 +75,6 @@ public final class UsageStats implements Parcelable { mTotalTimeInForeground = stats.mTotalTimeInForeground; mLaunchCount = stats.mLaunchCount; mLastEvent = stats.mLastEvent; - mBeginIdleTime = stats.mBeginIdleTime; - mLastTimeSystemUsed = stats.mLastTimeSystemUsed; } public String getPackageName() { @@ -127,25 +111,6 @@ public final class UsageStats implements Parcelable { } /** - * @hide - * Get the last time this package was used by the system (not the user). This can be different - * from {@link #getLastTimeUsed()} when the system binds to one of this package's services. - * See {@link System#currentTimeMillis()}. - */ - public long getLastTimeSystemUsed() { - return mLastTimeSystemUsed; - } - - /** - * @hide - * Get the last time this package was active, measured in milliseconds. This timestamp - * uses a timebase that represents how much the device was used and not wallclock time. - */ - public long getBeginIdleTime() { - return mBeginIdleTime; - } - - /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { @@ -172,8 +137,6 @@ public final class UsageStats implements Parcelable { // regards to their mEndTimeStamp. mLastEvent = right.mLastEvent; mLastTimeUsed = right.mLastTimeUsed; - mBeginIdleTime = right.mBeginIdleTime; - mLastTimeSystemUsed = right.mLastTimeSystemUsed; } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); @@ -195,8 +158,6 @@ public final class UsageStats implements Parcelable { dest.writeLong(mTotalTimeInForeground); dest.writeInt(mLaunchCount); dest.writeInt(mLastEvent); - dest.writeLong(mBeginIdleTime); - dest.writeLong(mLastTimeSystemUsed); } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -210,8 +171,6 @@ public final class UsageStats implements Parcelable { stats.mTotalTimeInForeground = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); - stats.mBeginIdleTime = in.readLong(); - stats.mLastTimeSystemUsed = in.readLong(); return stats; } diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 4135487a78d2..9221fbb50c96 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -148,8 +148,7 @@ public class ContentProviderClient implements AutoCloseable { return null; } - if ("com.google.android.gms".equals(mPackageName) - || "com.google.android.syncadapters.contacts".equals(mPackageName)) { + if ("com.google.android.gms".equals(mPackageName)) { // They're casting to a concrete subclass, sigh return cursor; } else { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 83943ade7d82..7dc74010aa77 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -8939,6 +8939,8 @@ public class Intent implements Parcelable, Cloneable { case ACTION_MEDIA_SCANNER_STARTED: case ACTION_MEDIA_SCANNER_FINISHED: case ACTION_MEDIA_SCANNER_SCAN_FILE: + case ACTION_PACKAGE_NEEDS_VERIFICATION: + case ACTION_PACKAGE_VERIFIED: // Ignore legacy actions break; default: diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 7db5a0889a35..be4f89567f51 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -715,7 +715,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration * about setLocales() has changed locale directly. */ private void fixUpLocaleList() { if ((locale == null && !mLocaleList.isEmpty()) || - (locale != null && !locale.equals(mLocaleList.getPrimary()))) { + (locale != null && !locale.equals(mLocaleList.get(0)))) { mLocaleList = new LocaleList(locale); } } @@ -1269,7 +1269,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration localeArray[i] = Locale.forLanguageTag(source.readString()); } mLocaleList = new LocaleList(localeArray); - locale = mLocaleList.getPrimary(); + locale = mLocaleList.get(0); userSetLocale = (source.readInt()==1); touchscreen = source.readInt(); @@ -1435,7 +1435,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public void setLocales(@Nullable LocaleList locales) { mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales; - locale = mLocaleList.getPrimary(); + locale = mLocaleList.get(0); setLayoutDirection(locale); } @@ -1900,7 +1900,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration final String localesStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALES); configOut.mLocaleList = LocaleList.forLanguageTags(localesStr); - configOut.locale = configOut.mLocaleList.getPrimary(); + configOut.locale = configOut.mLocaleList.get(0); configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN, TOUCHSCREEN_UNDEFINED); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 4967d05e2727..915fae0ff6b5 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -381,7 +381,7 @@ public class Resources { private PluralRules getPluralRule() { synchronized (sSync) { if (mPluralRule == null) { - mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary()); + mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0)); } return mPluralRule; } @@ -444,7 +444,7 @@ public class Resources { @NonNull public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException { final String raw = getString(id); - return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); + return String.format(mConfiguration.getLocales().get(0), raw, formatArgs); } /** @@ -475,7 +475,7 @@ public class Resources { public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs) throws NotFoundException { String raw = getQuantityText(id, quantity).toString(); - return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); + return String.format(mConfiguration.getLocales().get(0), raw, formatArgs); } /** @@ -1971,7 +1971,7 @@ public class Resources { } mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, - adjustLanguageTag(locales.getPrimary().toLanguageTag()), + adjustLanguageTag(locales.get(0).toLanguageTag()), mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, @@ -1996,7 +1996,7 @@ public class Resources { } synchronized (sSync) { if (mPluralRule != null) { - mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary()); + mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0)); } } } diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java index b3e3ab6251c8..44871212daef 100644 --- a/core/java/android/nfc/tech/NfcF.java +++ b/core/java/android/nfc/tech/NfcF.java @@ -98,8 +98,13 @@ public final class NfcF extends BasicTagTechnology { /** * Send raw NFC-F commands to the tag and receive the response. * - * <p>Applications must not append the SoD (length) or EoD (CRC) to the payload, - * it will be automatically calculated. + * <p>Applications must not prefix the SoD (preamble and sync code) + * and/or append the EoD (CRC) to the payload, it will be automatically calculated. + * + * <p>A typical NFC-F frame for this method looks like: + * <pre> + * LENGTH (1 byte) --- CMD (1 byte) -- IDm (8 bytes) -- PARAMS (LENGTH - 10 bytes) + * </pre> * * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes * that can be sent with {@link #transceive}. diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 52fa2ed162f1..b33e807235bf 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -147,6 +147,11 @@ public abstract class BatteryStats implements Parcelable { public static final int WAKE_TYPE_DRAW = 18; /** + * A constant indicating a bluetooth scan timer. + */ + public static final int BLUETOOTH_SCAN_ON = 19; + + /** * Include all of the data in the stats, including previously saved data. */ public static final int STATS_SINCE_CHARGED = 0; @@ -438,6 +443,7 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getFlashlightTurnedOnTimer(); public abstract Timer getCameraTurnedOnTimer(); public abstract Timer getForegroundActivityTimer(); + public abstract Timer getBluetoothScanTimer(); // Time this uid has any processes in the top state. public static final int PROCESS_STATE_TOP = 0; @@ -1179,6 +1185,7 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<23; public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22; public static final int STATE2_CAMERA_FLAG = 1<<21; + public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20; public static final int MOST_INTERESTING_STATES2 = STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK @@ -1922,6 +1929,7 @@ public abstract class BatteryStats implements Parcelable { HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, "wifi_suppl", "Wsp", WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES), new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"), + new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"), }; public static final String[] HISTORY_EVENT_NAMES = new String[] { @@ -2041,6 +2049,13 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getCameraOnTime(long elapsedRealtimeUs, int which); + /** + * Returns the time in microseconds that bluetooth scans were running while the device was + * on battery. + * + * {@hide} + */ + public abstract long getBluetoothScanTime(long elapsedRealtimeUs, int which); public static final int NETWORK_MOBILE_RX_DATA = 0; public static final int NETWORK_MOBILE_TX_DATA = 1; @@ -2797,9 +2812,12 @@ public abstract class BatteryStats implements Parcelable { final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which); final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which); final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which); + final long btRxTotalBytes = getNetworkActivityBytes(NETWORK_BT_RX_DATA, which); + final long btTxTotalBytes = getNetworkActivityBytes(NETWORK_BT_TX_DATA, which); dumpLine(pw, 0 /* uid */, category, GLOBAL_NETWORK_DATA, mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes, - mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets); + mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets, + btRxTotalBytes, btTxTotalBytes); // Dump Modem controller stats dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_MODEM_CONTROLLER_DATA, @@ -3017,14 +3035,18 @@ public abstract class BatteryStats implements Parcelable { final int mobileActiveCount = u.getMobileRadioActiveCount(which); final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which); final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which); + final long btBytesRx = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which); + final long btBytesTx = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which); if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0 || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0 - || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0) { + || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0 + || btBytesRx > 0 || btBytesTx > 0) { dumpLine(pw, uid, category, NETWORK_DATA, mobileBytesRx, mobileBytesTx, wifiBytesRx, wifiBytesTx, mobilePacketsRx, mobilePacketsTx, wifiPacketsRx, wifiPacketsTx, - mobileActiveTime, mobileActiveCount); + mobileActiveTime, mobileActiveCount, + btBytesRx, btBytesTx); } // Dump modem controller data, per UID. @@ -3046,6 +3068,9 @@ public abstract class BatteryStats implements Parcelable { dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA, u.getWifiControllerActivity(), which); + dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA, + u.getBluetoothControllerActivity(), which); + if (u.hasUserActivity()) { args = new Object[Uid.NUM_USER_ACTIVITY_TYPES]; boolean hasData = false; @@ -3668,6 +3693,12 @@ public abstract class BatteryStats implements Parcelable { pw.print(" Bluetooth total received: "); pw.print(formatBytesLocked(btRxTotalBytes)); pw.print(", sent: "); pw.println(formatBytesLocked(btTxTotalBytes)); + final long bluetoothScanTimeMs = getBluetoothScanTime(rawRealtime, which) / 1000; + sb.setLength(0); + sb.append(prefix); + sb.append(" Bluetooth scan time: "); formatTimeMs(sb, bluetoothScanTimeMs); + pw.println(sb.toString()); + printControllerActivity(pw, sb, prefix, "Bluetooth", getBluetoothControllerActivity(), which); @@ -3793,6 +3824,10 @@ public abstract class BatteryStats implements Parcelable { pw.print(" wifi="); printmAh(pw, bs.wifiPowerMah); } + if (bs.bluetoothPowerMah != 0) { + pw.print(" bt="); + printmAh(pw, bs.bluetoothPowerMah); + } if (bs.gpsPowerMah != 0) { pw.print(" gps="); printmAh(pw, bs.gpsPowerMah); @@ -4035,6 +4070,9 @@ public abstract class BatteryStats implements Parcelable { pw.println(" sent"); } + uidActivity |= printTimer(pw, sb, u.getBluetoothScanTimer(), rawRealtime, which, prefix, + "Bluetooth Scan"); + if (u.hasUserActivity()) { boolean hasData = false; for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) { diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 344d06ec3143..24666fe71724 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -74,6 +74,9 @@ public final class UserHandle implements Parcelable { /** @hide A user id constant to indicate the "system" user of the device */ public static final @UserIdInt int USER_SYSTEM = 0; + /** @hide A user serial constant to indicate the "system" user of the device */ + public static final int USER_SERIAL_SYSTEM = 0; + /** @hide A user handle to indicate the "system" user of the device */ public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 9b3f02d69e9f..dd8eb5f2c634 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1284,7 +1284,7 @@ public interface IMountService extends IInterface { @Override public void prepareUserStorage( - String volumeUuid, int userId, int serialNumber, boolean ephemeral) + String volumeUuid, int userId, int serialNumber, int flags) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1293,7 +1293,7 @@ public interface IMountService extends IInterface { _data.writeString(volumeUuid); _data.writeInt(userId); _data.writeInt(serialNumber); - _data.writeInt(ephemeral ? 1 : 0); + _data.writeInt(flags); mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0); _reply.readException(); } finally { @@ -2055,8 +2055,8 @@ public interface IMountService extends IInterface { String volumeUuid = data.readString(); int userId = data.readInt(); int serialNumber = data.readInt(); - boolean ephemeral = data.readInt() != 0; - prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral); + int _flags = data.readInt(); + prepareUserStorage(volumeUuid, userId, serialNumber, _flags); reply.writeNoException(); return true; } @@ -2389,7 +2389,7 @@ public interface IMountService extends IInterface { public boolean isUserKeyUnlocked(int userId) throws RemoteException; public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, - boolean ephemeral) throws RemoteException; + int flags) throws RemoteException; public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index c8b942bb27de..b82638aa005b 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -92,8 +92,14 @@ public class StorageManager { /** {@hide} */ public static final int DEBUG_EMULATE_FBE = 1 << 1; + // NOTE: keep in sync with installd /** {@hide} */ - public static final int FLAG_FOR_WRITE = 1 << 0; + public static final int FLAG_STORAGE_DE = 1 << 0; + /** {@hide} */ + public static final int FLAG_STORAGE_CE = 1 << 1; + + /** {@hide} */ + public static final int FLAG_FOR_WRITE = 1 << 8; private final Context mContext; private final ContentResolver mResolver; @@ -1003,10 +1009,9 @@ public class StorageManager { } /** {@hide} */ - public void prepareUserStorage( - String volumeUuid, int userId, int serialNumber, boolean ephemeral) { + public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { try { - mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral); + mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java index 7a9d0625c199..ea54f9203873 100644 --- a/core/java/android/provider/BlockedNumberContract.java +++ b/core/java/android/provider/BlockedNumberContract.java @@ -20,35 +20,126 @@ import android.net.Uri; import android.os.Bundle; /** - * Constants and methods to access blocked phone numbers for incoming calls and texts. + * <p> + * The contract between the blockednumber provider and applications. Contains definitions for + * the supported URIs and columns. + * </p> * - * TODO javadoc - * - Proper javadoc tagging. - * - Code sample? - * - Describe who can access it. + * <h3> Overview </h3> + * <p> + * The content provider exposes a table containing blocked numbers. The columns and URIs for + * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from + * blocked numbers are discarded by the platform. Notifications upon provider changes can be + * received using a {@link android.database.ContentObserver}. + * </p> + * + * <h3> Permissions </h3> + * <p> + * Only the system, the default SMS application, and the default phone app + * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps + * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber + * provider. + * </p> + * + * <h3> Data </h3> + * <p> + * Other than regular phone numbers, the blocked number provider can also store addresses (such + * as email) from which a user can receive messages, and calls. The blocked numbers are stored + * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone + * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER} + * column. The platform blocks calls, and messages from an address if it is present in in the + * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address + * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column. + * </p> + * + * <h3> Operations </h3> + * <dl> + * <dt><b>Insert</b></dt> + * <dd> + * <p> + * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated. + * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone + * number's E164 representation. The provider automatically populates this column if the app does + * not provide it. Note that this column is not populated if normalization fails or if the address + * is not a phone number (eg: email). The provider enforces uniqueness constraint on this column. + * Examples: + * <pre> + * ContentValues values = new ContentValues(); + * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); + * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); + * </pre> + * <pre> + * ContentValues values = new ContentValues(); + * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); + * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890"); + * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); + * </pre> + * <pre> + * ContentValues values = new ContentValues(); + * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345@abdcde.com"); + * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); + * </pre> + * </p> + * </dd> + * <dt><b>Update</b></dt> + * <dd> + * <p> + * Updates are not supported. Use Delete, and Insert instead. + * </p> + * </dd> + * <dt><b>Delete</b></dt> + * <dd> + * <p> + * Deletions can be performed as follows: + * <pre> + * ContentValues values = new ContentValues(); + * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890"); + * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values); + * getContentResolver().delete(uri, null, null); + * </pre> + * </p> + * </dd> + * <dt><b>Query</b></dt> + * <dd> + * <p> + * All blocked numbers can be enumerated as follows: + * <pre> + * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI, + * new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER, + * BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null); + * </pre> + * To check if a particular number is blocked, use the method + * {@link #isBlocked(Context, String)}. + * </p> + * </dd> + * + * <h3> Multi-user </h3> + * <p> + * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any + * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns + * {@code false}, all operations on the provider will fail with an + * {@link UnsupportedOperationException}. The platform will block calls, and messages from numbers + * in the provider independent of the current user. + * </p> */ public class BlockedNumberContract { private BlockedNumberContract() { } - /** The authority for the contacts provider */ + /** The authority for the blocked number provider */ public static final String AUTHORITY = "com.android.blockednumber"; - /** A content:// style uri to the authority for the contacts provider */ + /** A content:// style uri to the authority for the blocked number provider */ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); /** - * TODO javadoc - * - * Constants to interact with the blocked phone number list. + * Constants to interact with the blocked numbers list. */ public static class BlockedNumbers { private BlockedNumbers() { } /** - * TODO javadoc - * * Content URI for the blocked numbers. * * Supported operations @@ -117,8 +208,8 @@ public class BlockedNumberContract { * context {@code context}, this method will throw an {@link UnsupportedOperationException}. */ public static boolean isBlocked(Context context, String phoneNumber) { - final Bundle res = context.getContentResolver().call(AUTHORITY_URI, - METHOD_IS_BLOCKED, phoneNumber, null); + final Bundle res = context.getContentResolver().call( + AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null); return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false); } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 330fcf6c4c74..dfdd36d2e9cf 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -2079,10 +2079,11 @@ public final class ContactsContract { if (preferHighres) { final Uri displayPhotoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.DISPLAY_PHOTO); - InputStream inputStream; try { AssetFileDescriptor fd = cr.openAssetFileDescriptor(displayPhotoUri, "r"); - return fd.createInputStream(); + if (fd != null) { + return fd.createInputStream(); + } } catch (IOException e) { // fallback to the thumbnail code } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dfc86012c810..7830142d4e4b 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3869,7 +3869,6 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.DATA_ROAMING); MOVED_TO_GLOBAL.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.DEVICE_PROVISIONED); - MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_DENSITY_FORCED); MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_SIZE_FORCED); MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE); MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE); @@ -5104,6 +5103,15 @@ public final class Settings { "disabled_print_services"; /** + * The saved value for WindowManagerService.setForcedDisplayDensity() + * formatted as a single integer representing DPI. If unset, then use + * the real display density. + * + * @hide + */ + public static final String DISPLAY_DENSITY_FORCED = "display_density_forced"; + + /** * Setting to always use the default text-to-speech settings regardless * of the application settings. * 1 = override application settings, @@ -6535,13 +6543,6 @@ public final class Settings { public static final String DEVICE_PROVISIONED = "device_provisioned"; /** - * The saved value for WindowManagerService.setForcedDisplayDensity(). - * One integer in dpi. If unset, then use the real display density. - * @hide - */ - public static final String DISPLAY_DENSITY_FORCED = "display_density_forced"; - - /** * The saved value for WindowManagerService.setForcedDisplaySize(). * Two integers separated by a comma. If unset, then use the real display size. * @hide diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index a56e0301840f..fb58f4e519c3 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -45,7 +45,9 @@ import android.util.Log; * <action android:name="android.service.notification.NotificationAssistantService" /> * </intent-filter> * </service></pre> + * @hide */ +@SystemApi public abstract class NotificationAssistantService extends NotificationListenerService { private static final String TAG = "NotificationAssistant"; @@ -210,29 +212,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS } } - /** - * Add an annotation to a an existing notification. The delete intent will - * be fired when the host notification is deleted, or when this annotation - * is removed or replaced. - * - * @param key the key of the notification to be annotated - * @param annotation the new annotation object - */ - public final void setAnnotation(String key, Notification annotation) - { - // TODO: pack up the annotation and send it to the NotificationManager. - } - - /** - * Remove the annotation from a notification. - * - * @param key the key of the notification to be cleansed of annotatons - */ - public final void clearAnnotation(String key) - { - // TODO: ask the NotificationManager to clear the annotation. - } - private class NotificationAssistantWrapper extends NotificationListenerWrapper { @Override public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ed90e795af00..7ff883e1d127 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -695,7 +695,7 @@ public abstract class NotificationListenerService extends Service { /** * Request that the listener be rebound, after a previous call to (@link requestUnbind). * - * <P>This method will fail for assistants that have + * <P>This method will fail for listeners that have * not been granted the permission by the user. * * <P>The service should wait for the {@link #onListenerConnected()} event @@ -1022,8 +1022,7 @@ public abstract class NotificationListenerService extends Service { } /** - * If the importance has been overriden by user preference, or by a - * {@link NotificationAssistantService}, then this will be non-null, + * If the importance has been overriden by user preference, then this will be non-null, * and should be displayed to the user. * * @return the explanation for the importance, or null if it is the natural importance diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java index 117de77442fb..4f687c857362 100644 --- a/core/java/android/text/style/LocaleSpan.java +++ b/core/java/android/text/style/LocaleSpan.java @@ -97,12 +97,12 @@ public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan { * @return The {@link Locale} for this span. If multiple locales are associated with this * span, only the first locale is returned. {@code null} if no {@link Locale} is specified. * - * @see LocaleList#getPrimary() + * @see LocaleList#get() * @see #getLocales() */ @Nullable public Locale getLocale() { - return mLocales.getPrimary(); + return mLocales.get(0); } /** diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java index 90a20bc2ab24..fc39004cf2cb 100644 --- a/core/java/android/util/LocaleList.java +++ b/core/java/android/util/LocaleList.java @@ -47,12 +47,7 @@ public final class LocaleList implements Parcelable { private static final LocaleList sEmptyLocaleList = new LocaleList(); public Locale get(int location) { - return location < mList.length ? mList[location] : null; - } - - @Nullable - public Locale getPrimary() { - return mList.length == 0 ? null : get(0); + return (0 <= location && location < mList.length) ? mList[location] : null; } public boolean isEmpty() { @@ -464,7 +459,7 @@ public final class LocaleList implements Parcelable { // someone has called Locale.setDefault() since we last set or adjusted the default // locale list. So let's recalculate the locale list. if (sDefaultLocaleList != null - && defaultLocale.equals(sDefaultLocaleList.getPrimary())) { + && defaultLocale.equals(sDefaultLocaleList.get(0))) { // The default Locale has changed, but it happens to be the first locale in the // default locale list, so we don't need to construct a new locale list. return sDefaultLocaleList; diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 2aace0f30139..7017ff5a3fa0 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -136,6 +136,9 @@ public class RenderNode { private RenderNode(String name, View owningView) { mNativeRenderNode = nCreate(name); mOwningView = owningView; + if (mOwningView instanceof SurfaceView) { + nRequestPositionUpdates(mNativeRenderNode, (SurfaceView) mOwningView); + } } /** @@ -863,6 +866,8 @@ public class RenderNode { private static native void nOutput(long renderNode); private static native int nGetDebugSize(long renderNode); + private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback); + /////////////////////////////////////////////////////////////////////////// // Animations /////////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 5b48e2893fc7..a2960515da08 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -135,7 +135,7 @@ public class SurfaceView extends View { } }; - final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener + private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { @@ -143,6 +143,17 @@ public class SurfaceView extends View { } }; + private final ViewTreeObserver.OnPreDrawListener mDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // reposition ourselves where the surface is + mHaveFrame = getWidth() > 0 && getHeight() > 0; + updateWindow(false, false); + return true; + } + }; + boolean mRequestedVisible = false; boolean mWindowVisibility = false; boolean mViewVisibility = false; @@ -168,17 +179,9 @@ public class SurfaceView extends View { boolean mUpdateWindowNeeded; boolean mReportDrawNeeded; private Translator mTranslator; + private int mWindowInsetLeft; + private int mWindowInsetTop; - private final ViewTreeObserver.OnPreDrawListener mDrawListener = - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - // reposition ourselves where the surface is - mHaveFrame = getWidth() > 0 && getHeight() > 0; - updateWindow(false, false); - return true; - } - }; private boolean mGlobalListenersAdded; public SurfaceView(Context context) { @@ -443,17 +446,17 @@ public class SurfaceView extends View { int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - getLocationInWindow(mLocation); final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height; - final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1]; if (force || creating || formatChanged || sizeChanged || visibleChanged || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { + getLocationInWindow(mLocation); + if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged @@ -643,24 +646,66 @@ public class SurfaceView extends View { TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + " w=" + mLayout.width + " h=" + mLayout.height + ", frame=" + mSurfaceFrame); - } else if (positionChanged || layoutSizeChanged) { // Only the position has changed - mWindowSpaceLeft = mLocation[0]; - mWindowSpaceTop = mLocation[1]; - // For our size changed check, we keep mLayout.width and mLayout.height - // in view local space. - mLocation[0] = mLayout.width = getWidth(); - mLocation[1] = mLayout.height = getHeight(); + } else if (!isHardwareAccelerated()) { + getLocationInWindow(mLocation); + final boolean positionChanged = mWindowSpaceLeft != mLocation[0] + || mWindowSpaceTop != mLocation[1]; + if (positionChanged || layoutSizeChanged) { // Only the position has changed + mWindowSpaceLeft = mLocation[0]; + mWindowSpaceTop = mLocation[1]; + // For our size changed check, we keep mLayout.width and mLayout.height + // in view local space. + mLocation[0] = mLayout.width = getWidth(); + mLocation[1] = mLayout.height = getHeight(); - transformFromViewToWindowSpace(mLocation); + transformFromViewToWindowSpace(mLocation); - try { - mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop, - mLocation[0], mLocation[1], - viewRoot != null ? viewRoot.getNextFrameNumber() : -1, - mWinFrame); - } catch (RemoteException ex) { - Log.e(TAG, "Exception from relayout", ex); + try { + Log.d(TAG, String.format("updateWindowPosition UI, " + + "postion = [%d, %d, %d, %d]", mWindowSpaceLeft, mWindowSpaceTop, + mLocation[0], mLocation[1])); + mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop, + mLocation[0], mLocation[1], -1, mWinFrame); + } catch (RemoteException ex) { + Log.e(TAG, "Exception from relayout", ex); + } + } + } + } + + private Rect mRTLastReportedPosition = new Rect(); + + /** + * Called by native on RenderThread to update the window position + * @hide + */ + public final void updateWindowPositionRT(long frameNumber, + int left, int top, int right, int bottom) { + IWindowSession session = mSession; + MyWindow window = mWindow; + if (session == null || window == null) { + // Guess we got detached, that sucks + return; + } + if (mRTLastReportedPosition.left == left + && mRTLastReportedPosition.top == top + && mRTLastReportedPosition.right == right + && mRTLastReportedPosition.bottom == bottom) { + return; + } + try { + if (DEBUG) { + Log.d(TAG, String.format("updateWindowPosition RT, frameNr = %d, " + + "postion = [%d, %d, %d, %d]", frameNumber, left, top, + right, bottom)); } + // Just using mRTLastReportedPosition as a dummy rect here + session.repositionChild(window, left, top, right, bottom, frameNumber, + mRTLastReportedPosition); + // Now overwrite mRTLastReportedPosition with our values + mRTLastReportedPosition.set(left, top, right, bottom); + } catch (RemoteException ex) { + Log.e(TAG, "Exception from repositionChild", ex); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4a0a0b0dbeb7..127157b11a91 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4506,9 +4506,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } break; case R.styleable.View_pointerShape: - final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED); - if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) { - setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape)); + final int resourceId = a.getResourceId(attr, 0); + if (resourceId != 0) { + setPointerIcon(PointerIcon.loadCustomIcon( + context.getResources(), resourceId)); + } else { + final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED); + if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) { + setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape)); + } } break; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 98e32891e033..96853e0fa775 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1811,7 +1811,9 @@ public final class ViewRootImpl implements ViewParent, final boolean dragResizing = freeformResizing || dockedResizing; if (mDragResizing != dragResizing) { if (dragResizing) { - startDragResizing(mPendingBackDropFrame); + startDragResizing(mPendingBackDropFrame, + mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets, + mPendingStableInsets); mResizeMode = freeformResizing ? RESIZE_MODE_FREEFORM : RESIZE_MODE_DOCKED_DIVIDER; @@ -5845,9 +5847,11 @@ public final class ViewRootImpl implements ViewParent, // Tell all listeners that we are resizing the window so that the chrome can get // updated as fast as possible on a separate thread, if (mDragResizing) { + boolean fullscreen = frame.equals(backDropFrame); synchronized (mWindowCallbacks) { for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame); + mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen, + visibleInsets, stableInsets); } } } @@ -6815,20 +6819,6 @@ public final class ViewRootImpl implements ViewParent, } } - long getNextFrameNumber() { - long frameNumber = -1; - if (mSurfaceHolder != null) { - mSurfaceHolder.mSurfaceLock.lock(); - } - if (mSurface.isValid()) { - frameNumber = mSurface.getNextFrameNumber(); - } - if (mSurfaceHolder != null) { - mSurfaceHolder.mSurfaceLock.unlock(); - } - return frameNumber; - } - class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { @@ -7060,12 +7050,14 @@ public final class ViewRootImpl implements ViewParent, /** * Start a drag resizing which will inform all listeners that a window resize is taking place. */ - private void startDragResizing(Rect initialBounds) { + private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets) { if (!mDragResizing) { mDragResizing = true; synchronized (mWindowCallbacks) { for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds); + mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen, + systemInsets, stableInsets); } } mFullRedrawNeeded = true; diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java index def02365dfed..d2bfca7e1c30 100644 --- a/core/java/android/view/WindowCallbacks.java +++ b/core/java/android/view/WindowCallbacks.java @@ -28,20 +28,30 @@ import android.graphics.Rect; public interface WindowCallbacks { /** * Called by the system when the window got changed by the user, before the layouter got called. - * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to - * complete. + * It also gets called when the insets changed, or when the window switched between a fullscreen + * layout or a non-fullscreen layout. It can be used to perform a "quick and dirty" resize which + * should never take more then 4ms to complete. * * <p>At the time the layouting has not happened yet. * * @param newBounds The new window frame bounds. + * @param fullscreen Whether the window is currently drawing in fullscreen. + * @param systemInsets The current visible system insets for the window. + * @param stableInsets The stable insets for the window. */ - void onWindowSizeIsChanging(Rect newBounds); + void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets); /** * Called when a drag resize starts. + * * @param initialBounds The initial bounds where the window will be. + * @param fullscreen Whether the window is currently drawing in fullscreen. + * @param systemInsets The current visible system insets for the window. + * @param stableInsets The stable insets for the window. */ - void onWindowDragResizeStart(Rect initialBounds); + void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets); /** * Called when a drag resize ends. diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index 89b1eb933025..c22d60d94818 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -268,4 +268,9 @@ public abstract class WindowManagerInternal { /** Returns true if the stack with the input Id is currently visible. */ public abstract boolean isStackVisible(int stackId); + + /** + * @return True if and only if the docked divider is currently in resize mode. + */ + public abstract boolean isDockedDividerResizing(); } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 4ca8971b34ab..9a6859387bdf 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -955,7 +955,7 @@ public abstract class AbsSeekBar extends ProgressBar { if (!canUserSetProgress()) { return false; } - int increment = Math.max(1, Math.round((float) getMax() / 5)); + int increment = Math.max(1, Math.round((float) getMax() / 20)); if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { increment = -increment; } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index ee73092b3fb5..1321221eb89f 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -2404,7 +2404,7 @@ public class GridView extends AbsListView { } final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - final boolean isHeading = lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + final boolean isHeading = lp != null && lp.viewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER; final boolean isSelected = isItemChecked(position); final CollectionItemInfo itemInfo = CollectionItemInfo.obtain( row, 1, column, 1, isHeading, isSelected); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index cf13a13cafb7..b0fb93b89052 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -761,6 +761,7 @@ public class ChooserActivity extends ResolverActivity { public static final int TARGET_STANDARD = 2; private static final int MAX_SERVICE_TARGETS = 8; + private static final int MAX_TARGETS_PER_SERVICE = 4; private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); private final List<TargetInfo> mCallerTargets = new ArrayList<>(); @@ -925,7 +926,7 @@ public class ChooserActivity extends ResolverActivity { final float parentScore = getScore(origTarget); Collections.sort(targets, mBaseTargetComparator); float lastScore = 0; - for (int i = 0, N = targets.size(); i < N; i++) { + for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) { final ChooserTarget target = targets.get(i); float targetScore = target.getScore(); targetScore *= parentScore; diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index ec53a2ec6366..74fe94f96ddb 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -124,4 +124,5 @@ interface IBatteryStats { void noteBleScanStarted(in WorkSource ws); void noteBleScanStopped(in WorkSource ws); + void noteResetBleScan(); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 53e329d090cf..3fb768f68ee6 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -52,6 +52,7 @@ import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; import android.os.RemoteException; +import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -173,6 +174,10 @@ public class ResolverActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { + // We're dispatching intents that might be coming from legacy apps, so + // don't kill ourselves. + StrictMode.disableDeathOnFileUriExposure(); + // Use a specialized prompt when we're handling the 'Home' app startActivity() final Intent intent = makeMyIntent(); final Set<String> categories = intent.getCategories(); diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java index 0ae21c61112f..03a3a3821f6a 100644 --- a/core/java/com/android/internal/app/ResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverComparator.java @@ -52,7 +52,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12; - private static final float RECENCY_MULTIPLIER = 3.f; + private static final float RECENCY_MULTIPLIER = 2.f; private final Collator mCollator; private final boolean mHttp; diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 049d3eb26409..d92e59691fe9 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -44,6 +44,7 @@ public class BatterySipper implements Comparable<BatterySipper> { public long wakeLockTimeMs; public long cameraTimeMs; public long flashlightTimeMs; + public long bluetoothRunningTimeMs; public long mobileRxPackets; public long mobileTxPackets; @@ -56,6 +57,8 @@ public class BatterySipper implements Comparable<BatterySipper> { public long mobileTxBytes; public long wifiRxBytes; public long wifiTxBytes; + public long btRxBytes; + public long btTxBytes; public double percent; public double noCoveragePercent; public String[] mPackages; @@ -71,6 +74,7 @@ public class BatterySipper implements Comparable<BatterySipper> { public double sensorPowerMah; public double cameraPowerMah; public double flashlightPowerMah; + public double bluetoothPowerMah; public enum DrainType { IDLE, @@ -142,6 +146,7 @@ public class BatterySipper implements Comparable<BatterySipper> { wakeLockTimeMs += other.wakeLockTimeMs; cameraTimeMs += other.cameraTimeMs; flashlightTimeMs += other.flashlightTimeMs; + bluetoothRunningTimeMs += other.bluetoothRunningTimeMs; mobileRxPackets += other.mobileRxPackets; mobileTxPackets += other.mobileTxPackets; mobileActive += other.mobileActive; @@ -152,6 +157,8 @@ public class BatterySipper implements Comparable<BatterySipper> { mobileTxBytes += other.mobileTxBytes; wifiRxBytes += other.wifiRxBytes; wifiTxBytes += other.wifiTxBytes; + btRxBytes += other.btRxBytes; + btTxBytes += other.btTxBytes; wifiPowerMah += other.wifiPowerMah; gpsPowerMah += other.gpsPowerMah; cpuPowerMah += other.cpuPowerMah; @@ -160,6 +167,7 @@ public class BatterySipper implements Comparable<BatterySipper> { wakeLockPowerMah += other.wakeLockPowerMah; cameraPowerMah += other.cameraPowerMah; flashlightPowerMah += other.flashlightPowerMah; + bluetoothPowerMah += other.bluetoothPowerMah; } /** @@ -169,6 +177,6 @@ public class BatterySipper implements Comparable<BatterySipper> { public double sumPower() { return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + - flashlightPowerMah; + flashlightPowerMah + bluetoothPowerMah; } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e2ccaae1e87f..648b1a54927d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -107,7 +107,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 140 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 141 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -186,8 +186,13 @@ public final class BatteryStatsImpl extends BatteryStats { } public interface ExternalStatsSync { - void scheduleSync(String reason); - void scheduleWifiSync(String reason); + public static final int UPDATE_CPU = 0x01; + public static final int UPDATE_WIFI = 0x02; + public static final int UPDATE_RADIO = 0x04; + public static final int UPDATE_BT = 0x08; + public static final int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT; + + void scheduleSync(String reason, int flags); void scheduleCpuSyncDueToRemovedUid(int uid); } @@ -224,6 +229,7 @@ public final class BatteryStatsImpl extends BatteryStats { final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>(); final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>(); final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>(); + final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>(); // Last partial timers we use for distributing CPU usage. final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>(); @@ -435,6 +441,9 @@ public final class BatteryStatsImpl extends BatteryStats { final StopwatchTimer[] mWifiSignalStrengthsTimer = new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS]; + int mBluetoothScanNesting; + StopwatchTimer mBluetoothScanTimer; + int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; long mMobileRadioActiveStartTime; StopwatchTimer mMobileRadioActiveTimer; @@ -3714,7 +3723,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mWifiOn = true; mWifiOnTimer.startRunningLocked(elapsedRealtime); - scheduleSyncExternalWifiStatsLocked("wifi-off"); + scheduleSyncExternalStatsLocked("wifi-off", ExternalStatsSync.UPDATE_WIFI); } } @@ -3728,7 +3737,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mWifiOn = false; mWifiOnTimer.stopRunningLocked(elapsedRealtime); - scheduleSyncExternalWifiStatsLocked("wifi-on"); + scheduleSyncExternalStatsLocked("wifi-on", ExternalStatsSync.UPDATE_WIFI); } } @@ -3946,6 +3955,65 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private void noteBluetoothScanStartedLocked(int uid) { + uid = mapUid(uid); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + if (mBluetoothScanNesting == 0) { + mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: " + + Integer.toHexString(mHistoryCur.states2)); + addHistoryRecordLocked(elapsedRealtime, uptime); + } + mBluetoothScanNesting++; + getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime); + } + + public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws) { + final int N = ws.size(); + for (int i = 0; i < N; i++) { + noteBluetoothScanStartedLocked(ws.get(i)); + } + } + + private void noteBluetoothScanStoppedLocked(int uid) { + uid = mapUid(uid); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + mBluetoothScanNesting--; + if (mBluetoothScanNesting == 0) { + mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: " + + Integer.toHexString(mHistoryCur.states2)); + addHistoryRecordLocked(elapsedRealtime, uptime); + } + getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime); + } + + public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws) { + final int N = ws.size(); + for (int i = 0; i < N; i++) { + noteBluetoothScanStoppedLocked(ws.get(i)); + } + } + + public void noteResetBluetoothScanLocked() { + if (mBluetoothScanNesting > 0) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + mBluetoothScanNesting = 0; + mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: " + + Integer.toHexString(mHistoryCur.states2)); + addHistoryRecordLocked(elapsedRealtime, uptime); + mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtime); + for (int i=0; i<mUidStats.size(); i++) { + BatteryStatsImpl.Uid uid = mUidStats.valueAt(i); + uid.noteResetBluetoothScanLocked(elapsedRealtime); + } + } + } + public void noteWifiRadioPowerState(int powerState, long timestampNs) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -3980,7 +4048,7 @@ public final class BatteryStatsImpl extends BatteryStats { int uid = mapUid(ws.get(i)); getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime); } - scheduleSyncExternalWifiStatsLocked("wifi-running"); + scheduleSyncExternalStatsLocked("wifi-running", ExternalStatsSync.UPDATE_WIFI); } else { Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running"); } @@ -4019,7 +4087,7 @@ public final class BatteryStatsImpl extends BatteryStats { int uid = mapUid(ws.get(i)); getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime); } - scheduleSyncExternalWifiStatsLocked("wifi-stopped"); + scheduleSyncExternalStatsLocked("wifi-stopped", ExternalStatsSync.UPDATE_WIFI); } else { Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running"); } @@ -4034,7 +4102,7 @@ public final class BatteryStatsImpl extends BatteryStats { } mWifiState = wifiState; mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime); - scheduleSyncExternalWifiStatsLocked("wifi-state"); + scheduleSyncExternalStatsLocked("wifi-state", ExternalStatsSync.UPDATE_WIFI); } } @@ -4530,6 +4598,11 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public long getBluetoothScanTime(long elapsedRealtimeUs, int which) { + return mBluetoothScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + + @Override public long getNetworkActivityBytes(int type, int which) { if (type >= 0 && type < mNetworkByteActivityCounters.length) { return mNetworkByteActivityCounters[type].getCountLocked(which); @@ -4603,9 +4676,8 @@ public final class BatteryStatsImpl extends BatteryStats { StopwatchTimer mVideoTurnedOnTimer; StopwatchTimer mFlashlightTurnedOnTimer; StopwatchTimer mCameraTurnedOnTimer; - - StopwatchTimer mForegroundActivityTimer; + StopwatchTimer mBluetoothScanTimer; int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; StopwatchTimer[] mProcessStateTimer; @@ -4997,6 +5069,30 @@ public final class BatteryStatsImpl extends BatteryStats { return mForegroundActivityTimer; } + public StopwatchTimer createBluetoothScanTimerLocked() { + if (mBluetoothScanTimer == null) { + mBluetoothScanTimer = new StopwatchTimer(Uid.this, BLUETOOTH_SCAN_ON, + mBluetoothScanOnTimers, mOnBatteryTimeBase); + } + return mBluetoothScanTimer; + } + + public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs) { + createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs); + } + + public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs) { + if (mBluetoothScanTimer != null) { + mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs); + } + } + + public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) { + if (mBluetoothScanTimer != null) { + mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs); + } + } + @Override public void noteActivityResumedLocked(long elapsedRealtimeMs) { // We always start, since we want multiple foreground PIDs to nest @@ -5110,6 +5206,11 @@ public final class BatteryStatsImpl extends BatteryStats { return mForegroundActivityTimer; } + @Override + public Timer getBluetoothScanTimer() { + return mBluetoothScanTimer; + } + void makeProcessState(int i, Parcel in) { if (i < 0 || i >= NUM_PROCESS_STATE) return; @@ -5335,6 +5436,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (mForegroundActivityTimer != null) { active |= !mForegroundActivityTimer.reset(false); } + if (mBluetoothScanTimer != null) { + active |= !mBluetoothScanTimer.reset(false); + } if (mProcessStateTimer != null) { for (int i = 0; i < NUM_PROCESS_STATE; i++) { if (mProcessStateTimer[i] != null) { @@ -5509,6 +5613,10 @@ public final class BatteryStatsImpl extends BatteryStats { mForegroundActivityTimer.detach(); mForegroundActivityTimer = null; } + if (mBluetoothScanTimer != null) { + mBluetoothScanTimer.detach(); + mBluetoothScanTimer = null; + } if (mUserActivityCounters != null) { for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) { mUserActivityCounters[i].detach(); @@ -5669,6 +5777,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + if (mBluetoothScanTimer != null) { + out.writeInt(1); + mBluetoothScanTimer.writeToParcel(out, elapsedRealtimeUs); + } else { + out.writeInt(0); + } for (int i = 0; i < NUM_PROCESS_STATE; i++) { if (mProcessStateTimer[i] != null) { out.writeInt(1); @@ -5874,6 +5988,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { mForegroundActivityTimer = null; } + if (in.readInt() != 0) { + mBluetoothScanTimer = new StopwatchTimer(Uid.this, BLUETOOTH_SCAN_ON, + mBluetoothScanOnTimers, mOnBatteryTimeBase, in); + } else { + mBluetoothScanTimer = null; + } mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; for (int i = 0; i < NUM_PROCESS_STATE; i++) { if (in.readInt() != 0) { @@ -7133,6 +7253,7 @@ public final class BatteryStatsImpl extends BatteryStats { mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase); mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase); mCameraOnTimer = new StopwatchTimer(null, -13, null, mOnBatteryTimeBase); + mBluetoothScanTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase); mOnBattery = mOnBatteryInternal = false; long uptime = SystemClock.uptimeMillis() * 1000; long realtime = SystemClock.elapsedRealtime() * 1000; @@ -7732,6 +7853,7 @@ public final class BatteryStatsImpl extends BatteryStats { mVideoOnTimer.reset(false); mFlashlightOnTimer.reset(false); mCameraOnTimer.reset(false); + mBluetoothScanTimer.reset(false); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].reset(false); } @@ -8264,41 +8386,168 @@ public final class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, "Updating bluetooth stats: " + info); } - if (info != null && mOnBatteryInternal) { - mHasBluetoothReporting = true; - mBluetoothActivity.getRxTimeCounter().addCountLocked( - info.getControllerRxTimeMillis()); - mBluetoothActivity.getTxTimeCounters()[0].addCountLocked( - info.getControllerTxTimeMillis()); - mBluetoothActivity.getIdleTimeCounter().addCountLocked( - info.getControllerIdleTimeMillis()); + if (info == null || !mOnBatteryInternal) { + return; + } - // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. - final double opVolt = mPowerProfile.getAveragePower( - PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; - if (opVolt != 0) { - // We store the power drain as mAms. - mBluetoothActivity.getPowerCounter().addCountLocked( - (long) (info.getControllerEnergyUsed() / opVolt)); + mHasBluetoothReporting = true; + + final long elapsedRealtimeMs = SystemClock.elapsedRealtime(); + final long rxTimeMs = info.getControllerRxTimeMillis(); + final long txTimeMs = info.getControllerTxTimeMillis(); + + if (DEBUG_ENERGY) { + Slog.d(TAG, "------ BEGIN BLE power blaming ------"); + Slog.d(TAG, " Tx Time: " + txTimeMs + " ms"); + Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms"); + Slog.d(TAG, " Idle Time: " + info.getControllerIdleTimeMillis() + " ms"); + } + + long totalScanTimeMs = 0; + + final int uidCount = mUidStats.size(); + for (int i = 0; i < uidCount; i++) { + final Uid u = mUidStats.valueAt(i); + if (u.mBluetoothScanTimer == null) { + continue; } - final UidTraffic[] uidTraffic = info.getUidTraffic(); - final int numUids = uidTraffic != null ? uidTraffic.length : 0; + totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + } + + final boolean normalizeScanRxTime = (totalScanTimeMs > rxTimeMs); + final boolean normalizeScanTxTime = (totalScanTimeMs > txTimeMs); + + if (DEBUG_ENERGY) { + Slog.d(TAG, "Normalizing scan power for RX=" + normalizeScanRxTime + + " TX=" + normalizeScanTxTime); + } + + long leftOverRxTimeMs = rxTimeMs; + long leftOverTxTimeMs = txTimeMs; + + for (int i = 0; i < uidCount; i++) { + final Uid u = mUidStats.valueAt(i); + if (u.mBluetoothScanTimer == null) { + continue; + } + + long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + if (scanTimeSinceMarkMs > 0) { + // Set the new mark so that next time we get new data since this point. + u.mBluetoothScanTimer.setMark(elapsedRealtimeMs); + + long scanTimeRxSinceMarkMs = scanTimeSinceMarkMs; + long scanTimeTxSinceMarkMs = scanTimeSinceMarkMs; + + if (normalizeScanRxTime) { + // Scan time is longer than the total rx time in the controller, + // so distribute the scan time proportionately. This means regular traffic + // will not blamed, but scans are more expensive anyways. + scanTimeRxSinceMarkMs = (rxTimeMs * scanTimeRxSinceMarkMs) / totalScanTimeMs; + } + + if (normalizeScanTxTime) { + // Scan time is longer than the total tx time in the controller, + // so distribute the scan time proportionately. This means regular traffic + // will not blamed, but scans are more expensive anyways. + scanTimeTxSinceMarkMs = (txTimeMs * scanTimeTxSinceMarkMs) / totalScanTimeMs; + } + + final ControllerActivityCounterImpl counter = + u.getOrCreateBluetoothControllerActivityLocked(); + counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs); + counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs); + + leftOverRxTimeMs -= scanTimeRxSinceMarkMs; + leftOverTxTimeMs -= scanTimeTxSinceMarkMs; + } + } + + if (DEBUG_ENERGY) { + Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs + + " TX=" + leftOverTxTimeMs); + } + + // + // Now distribute blame to apps that did bluetooth traffic. + // + + long totalTxBytes = 0; + long totalRxBytes = 0; + + final UidTraffic[] uidTraffic = info.getUidTraffic(); + final int numUids = uidTraffic != null ? uidTraffic.length : 0; + for (int i = 0; i < numUids; i++) { + final UidTraffic traffic = uidTraffic[i]; + + // Add to the global counters. + mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked( + traffic.getRxBytes()); + mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked( + traffic.getTxBytes()); + + // Add to the UID counters. + final Uid u = getUidStatsLocked(mapUid(traffic.getUid())); + u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, traffic.getRxBytes(), 0); + u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, traffic.getTxBytes(), 0); + + // Calculate the total traffic. + totalTxBytes += traffic.getTxBytes(); + totalRxBytes += traffic.getRxBytes(); + } + + if ((totalTxBytes != 0 || totalRxBytes != 0) && + (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) { for (int i = 0; i < numUids; i++) { final UidTraffic traffic = uidTraffic[i]; - // Add to the global counters. - mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked( - traffic.getRxBytes()); - mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked( - traffic.getTxBytes()); - - // Add to the UID counters. final Uid u = getUidStatsLocked(mapUid(traffic.getUid())); - u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, traffic.getRxBytes(), 0); - u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, traffic.getTxBytes(), 0); + final ControllerActivityCounterImpl counter = + u.getOrCreateBluetoothControllerActivityLocked(); + + if (totalRxBytes > 0 && traffic.getRxBytes() > 0) { + final long timeRxMs = (leftOverRxTimeMs * traffic.getRxBytes()) / totalRxBytes; + + if (DEBUG_ENERGY) { + Slog.d(TAG, "UID=" + traffic.getUid() + " rx_bytes=" + traffic.getRxBytes() + + " rx_time=" + timeRxMs); + } + counter.getRxTimeCounter().addCountLocked(timeRxMs); + leftOverRxTimeMs -= timeRxMs; + } + + if (totalTxBytes > 0 && traffic.getTxBytes() > 0) { + final long timeTxMs = (leftOverTxTimeMs * traffic.getTxBytes()) / totalTxBytes; + + if (DEBUG_ENERGY) { + Slog.d(TAG, "UID=" + traffic.getUid() + " tx_bytes=" + traffic.getTxBytes() + + " tx_time=" + timeTxMs); + } + + counter.getTxTimeCounters()[0].addCountLocked(timeTxMs); + leftOverTxTimeMs -= timeTxMs; + } } } + + mBluetoothActivity.getRxTimeCounter().addCountLocked( + info.getControllerRxTimeMillis()); + mBluetoothActivity.getTxTimeCounters()[0].addCountLocked( + info.getControllerTxTimeMillis()); + mBluetoothActivity.getIdleTimeCounter().addCountLocked( + info.getControllerIdleTimeMillis()); + + // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. + final double opVolt = mPowerProfile.getAveragePower( + PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + if (opVolt != 0) { + // We store the power drain as mAms. + mBluetoothActivity.getPowerCounter().addCountLocked( + (long) (info.getControllerEnergyUsed() / opVolt)); + } } /** @@ -8739,15 +8988,9 @@ public final class BatteryStatsImpl extends BatteryStats { } } - private void scheduleSyncExternalStatsLocked(String reason) { - if (mExternalSync != null) { - mExternalSync.scheduleSync(reason); - } - } - - private void scheduleSyncExternalWifiStatsLocked(String reason) { + private void scheduleSyncExternalStatsLocked(String reason, int updateFlags) { if (mExternalSync != null) { - mExternalSync.scheduleWifiSync(reason); + mExternalSync.scheduleSync(reason, updateFlags); } } @@ -8815,7 +9058,7 @@ public final class BatteryStatsImpl extends BatteryStats { // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record // which will pull external stats. - scheduleSyncExternalStatsLocked("battery-level"); + scheduleSyncExternalStatsLocked("battery-level", ExternalStatsSync.UPDATE_ALL); } if (mHistoryCur.batteryStatus != status) { mHistoryCur.batteryStatus = (byte)status; @@ -9596,6 +9839,8 @@ public final class BatteryStatsImpl extends BatteryStats { mFlashlightOnTimer.readSummaryFromParcelLocked(in); mCameraOnNesting = 0; mCameraOnTimer.readSummaryFromParcelLocked(in); + mBluetoothScanNesting = 0; + mBluetoothScanTimer.readSummaryFromParcelLocked(in); int NKW = in.readInt(); if (NKW > 10000) { @@ -9666,6 +9911,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (in.readInt() != 0) { u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in); } + if (in.readInt() != 0) { + u.createBluetoothScanTimerLocked().readSummaryFromParcelLocked(in); + } u.mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) { if (in.readInt() != 0) { @@ -9928,6 +10176,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mNumConnectivityChange); mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); out.writeInt(mKernelWakelockStats.size()); for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { @@ -10021,6 +10270,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + if (u.mBluetoothScanTimer != null) { + out.writeInt(1); + u.mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + } else { + out.writeInt(0); + } for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) { if (u.mProcessStateTimer[i] != null) { out.writeInt(1); @@ -10289,6 +10544,8 @@ public final class BatteryStatsImpl extends BatteryStats { mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in); mCameraOnNesting = 0; mCameraOnTimer = new StopwatchTimer(null, -13, null, mOnBatteryTimeBase, in); + mBluetoothScanNesting = 0; + mBluetoothScanTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase, in); mDischargeUnplugLevel = in.readInt(); mDischargePlugLevel = in.readInt(); mDischargeCurrentLevel = in.readInt(); @@ -10436,6 +10693,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mUnpluggedNumConnectivityChange); mFlashlightOnTimer.writeToParcel(out, uSecRealtime); mCameraOnTimer.writeToParcel(out, uSecRealtime); + mBluetoothScanTimer.writeToParcel(out, uSecRealtime); out.writeInt(mDischargeUnplugLevel); out.writeInt(mDischargePlugLevel); out.writeInt(mDischargeCurrentLevel); diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 531d1fac9991..2f383eacbac0 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -24,6 +24,8 @@ public class BluetoothPowerCalculator extends PowerCalculator { private final double mIdleMa; private final double mRxMa; private final double mTxMa; + private double mAppTotalPowerMah = 0; + private long mAppTotalTimeMs = 0; public BluetoothPowerCalculator(PowerProfile profile) { mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE); @@ -34,7 +36,31 @@ public class BluetoothPowerCalculator extends PowerCalculator { @Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { - // No per-app distribution yet. + + final BatteryStats.ControllerActivityCounter counter = u.getBluetoothControllerActivity(); + if (counter == null) { + return; + } + + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); + final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs; + double powerMah = counter.getPowerCounter().getCountLocked(statsType) + / (double)(1000*60*60); + + if (powerMah == 0) { + powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) + / (1000*60*60); + } + + app.bluetoothPowerMah = powerMah; + app.bluetoothRunningTimeMs = totalTimeMs; + app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType); + app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType); + + mAppTotalPowerMah += powerMah; + mAppTotalTimeMs += totalTimeMs; } @Override @@ -56,12 +82,21 @@ public class BluetoothPowerCalculator extends PowerCalculator { / (1000*60*60); } + // Subtract what the apps used, but clamp to 0. + powerMah = Math.max(0, powerMah - mAppTotalPowerMah); + if (DEBUG && powerMah != 0) { Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs) + " power=" + BatteryStatsHelper.makemAh(powerMah)); } - app.usagePowerMah = powerMah; - app.usageTimeMs = totalTimeMs; + app.bluetoothPowerMah = powerMah; + app.bluetoothRunningTimeMs = Math.max(0, totalTimeMs - mAppTotalTimeMs); + } + + @Override + public void reset() { + mAppTotalPowerMah = 0; + mAppTotalTimeMs = 0; } } diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 2a27f70ff05d..b4470397c67b 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -29,6 +29,7 @@ public class WifiPowerCalculator extends PowerCalculator { private final double mTxCurrentMa; private final double mRxCurrentMa; private double mTotalAppPowerDrain = 0; + private long mTotalAppRunningTime = 0; public WifiPowerCalculator(PowerProfile profile) { mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); @@ -48,6 +49,8 @@ public class WifiPowerCalculator extends PowerCalculator { final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); app.wifiRunningTimeMs = idleTime + rxTime + txTime; + mTotalAppRunningTime += app.wifiRunningTimeMs; + app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000*60*60); @@ -76,7 +79,9 @@ public class WifiPowerCalculator extends PowerCalculator { final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs; + + app.wifiRunningTimeMs = Math.max(0, + (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime); double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType) / (double)(1000*60*60); @@ -95,5 +100,6 @@ public class WifiPowerCalculator extends PowerCalculator { @Override public void reset() { mTotalAppPowerDrain = 0; + mTotalAppRunningTime = 0; } } diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 5047c4c11889..69311938c5ad 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -24,7 +24,6 @@ import android.view.Choreographer; import android.view.DisplayListCanvas; import android.view.RenderNode; import android.view.ThreadedRenderer; -import android.view.View; /** * The thread which draws a fill in background while the app is resizing in areas where the app @@ -49,6 +48,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame private final Rect mOldTargetRect = new Rect(); private final Rect mNewTargetRect = new Rect(); + private Choreographer mChoreographer; // Cached size values from the last render for the case that the view hierarchy is gone @@ -66,15 +66,23 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame private Drawable mUserCaptionBackgroundDrawable; private Drawable mResizingBackgroundDrawable; private ColorDrawable mStatusBarColor; + private ColorDrawable mNavigationBarColor; + private boolean mOldFullscreen; + private boolean mFullscreen; + private final Rect mOldSystemInsets = new Rect(); + private final Rect mOldStableInsets = new Rect(); + private final Rect mSystemInsets = new Rect(); + private final Rect mStableInsets = new Rect(); public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable, - Drawable userCaptionBackgroundDrawable, int statusBarColor) { + Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor, + boolean fullscreen, Rect systemInsets, Rect stableInsets) { setName("ResizeFrame"); mRenderer = renderer; onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable, - userCaptionBackgroundDrawable, statusBarColor); + userCaptionBackgroundDrawable, statusBarColor, navigationBarColor); // Create a render node for the content and frame backdrop // which can be resized independently from the content. @@ -84,8 +92,14 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame // Set the initial bounds and draw once so that we do not get a broken frame. mTargetRect.set(initialBounds); + mFullscreen = fullscreen; + mOldFullscreen = fullscreen; + mSystemInsets.set(systemInsets); + mStableInsets.set(stableInsets); + mOldSystemInsets.set(systemInsets); + mOldStableInsets.set(stableInsets); synchronized (this) { - changeWindowSizeLocked(initialBounds); + redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets); } // Kick off our draw thread. @@ -94,7 +108,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable, - int statusBarColor) { + int statusBarColor, int navigationBarColor) { mDecorView = decorView; mResizingBackgroundDrawable = resizingBackgroundDrawable; mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable; @@ -108,6 +122,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame } else { mStatusBarColor = null; } + if (navigationBarColor != 0) { + mNavigationBarColor = new ColorDrawable(navigationBarColor); + addSystemBarNodeIfNeeded(); + } else { + mNavigationBarColor = null; + } } private void addSystemBarNodeIfNeeded() { @@ -119,13 +139,22 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame } /** - * Call this function asynchronously when the window size has been changed. The change will - * be picked up once per frame and the frame will be re-rendered accordingly. + * Call this function asynchronously when the window size has been changed or when the insets + * have changed or whether window switched between a fullscreen or non-fullscreen layout. + * The change will be picked up once per frame and the frame will be re-rendered accordingly. + * * @param newTargetBounds The new target bounds. + * @param fullscreen Whether the window is currently drawing in fullscreen. + * @param systemInsets The current visible system insets for the window. + * @param stableInsets The stable insets for the window. */ - public void setTargetRect(Rect newTargetBounds) { + public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets) { synchronized (this) { + mFullscreen = fullscreen; mTargetRect.set(newTargetBounds); + mSystemInsets.set(systemInsets); + mStableInsets.set(stableInsets); // Notify of a bounds change. pingRenderLocked(); } @@ -204,16 +233,23 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame return; } mNewTargetRect.set(mTargetRect); - if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) { + if (!mNewTargetRect.equals(mOldTargetRect) + || mOldFullscreen != mFullscreen + || !mStableInsets.equals(mOldStableInsets) + || !mSystemInsets.equals(mOldSystemInsets) + || mReportNextDraw) { + mOldFullscreen = mFullscreen; mOldTargetRect.set(mNewTargetRect); - changeWindowSizeLocked(mNewTargetRect); + mOldSystemInsets.set(mSystemInsets); + mOldStableInsets.set(mStableInsets); + redrawLocked(mNewTargetRect, mFullscreen, mSystemInsets, mStableInsets); } } } /** * The content is about to be drawn and we got the location of where it will be shown. - * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call + * If a "redrawLocked" call has already been processed, we will re-issue the call * if the previous call was ignored since the size was unknown. * @param xOffset The x offset where the content is drawn to. * @param yOffset The y offset where the content is drawn to. @@ -235,8 +271,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mLastYOffset, mLastXOffset + mLastContentWidth, mLastYOffset + mLastCaptionHeight + mLastContentHeight); - // If this was the first call and changeWindowSizeLocked got already called prior - // to us, we should re-issue a changeWindowSizeLocked now. + // If this was the first call and redrawLocked got already called prior + // to us, we should re-issue a redrawLocked now. return firstCall && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption()); } @@ -251,16 +287,20 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame } /** - * Resizing the frame to fit the new window size. + * Redraws the background, the caption and the system inset backgrounds if something changed. + * * @param newBounds The window bounds which needs to be drawn. + * @param fullscreen Whether the window is currently drawing in fullscreen. + * @param systemInsets The current visible system insets for the window. + * @param stableInsets The stable insets for the window. */ - private void changeWindowSizeLocked(Rect newBounds) { + private void redrawLocked(Rect newBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets) { // While a configuration change is taking place the view hierarchy might become // inaccessible. For that case we remember the previous metrics to avoid flashes. // Note that even when there is no visible caption, the caption child will exist. final int captionHeight = mDecorView.getCaptionHeight(); - final int statusBarHeight = mDecorView.getStatusBarHeight(); // The caption height will probably never dynamically change while we are resizing. // Once set to something other then 0 it should be kept that way. @@ -302,14 +342,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame } mFrameAndBackdropNode.end(canvas); - if (mSystemBarBackgroundNode != null && mStatusBarColor != null) { - canvas = mSystemBarBackgroundNode.start(width, height); - mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height); - mStatusBarColor.setBounds(0, 0, left + width, statusBarHeight); - mStatusBarColor.draw(canvas); - mSystemBarBackgroundNode.end(canvas); - mRenderer.drawRenderNode(mSystemBarBackgroundNode); - } + drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets); // We need to render the node explicitly mRenderer.drawRenderNode(mFrameAndBackdropNode); @@ -317,6 +350,39 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame reportDrawIfNeeded(); } + private void drawColorViews(int left, int top, int width, int height, + boolean fullscreen, Rect systemInsets, Rect stableInsets) { + if (mSystemBarBackgroundNode == null) { + return; + } + 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); + if (mStatusBarColor != null) { + mStatusBarColor.setBounds(0, 0, left + width, topInset); + mStatusBarColor.draw(canvas); + } + + // We only want to draw the navigation bar if our window is currently fullscreen because we + // 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); + if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) { + mNavigationBarColor.setBounds(width - size, 0, width, height); + } else { + mNavigationBarColor.setBounds(0, height - size, width, height); + } + mNavigationBarColor.draw(canvas); + } + mSystemBarBackgroundNode.end(canvas); + mRenderer.drawRenderNode(mSystemBarBackgroundNode); + } + /** Notify view root that a frame has been drawn by us, if it has requested so. */ private void reportDrawIfNeeded() { if (mReportNextDraw) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1a20e5ce5793..d4ada957a14a 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -922,6 +922,26 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return false; } + static int getColorViewTopInset(int stableTop, int systemTop) { + return Math.min(stableTop, systemTop); + } + + static int getColorViewBottomInset(int stableBottom, int systemBottom) { + return Math.min(stableBottom, systemBottom); + } + + static int getColorViewRightInset(int stableRight, int systemRight) { + return Math.min(stableRight, systemRight); + } + + static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { + return bottomInset == 0 && rightInset > 0; + } + + static int getNavBarSize(int bottomInset, int rightInset) { + return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset; + } + WindowInsets updateColorViews(WindowInsets insets, boolean animate) { WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); @@ -933,11 +953,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mLastWindowFlags = attrs.flags; if (insets != null) { - mLastTopInset = Math.min(insets.getStableInsetTop(), + mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(), insets.getSystemWindowInsetTop()); - mLastBottomInset = Math.min(insets.getStableInsetBottom(), + mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(), insets.getSystemWindowInsetBottom()); - mLastRightInset = Math.min(insets.getStableInsetRight(), + mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(), insets.getSystemWindowInsetRight()); // Don't animate if the presence of stable insets has changed, because that @@ -956,8 +976,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mLastHasRightStableInset = hasRightStableInset; } - boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0; - int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset; + boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset); + int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset); updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge, 0 /* rightInset */, animate && !disallowAnimate, false /* force */); @@ -1041,14 +1061,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind */ private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) { - state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0 + 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); - boolean showView = show && !isResizing(); + boolean showView = show && !isResizing() && size > 0; boolean visibilityChanged = false; View view = state.view; @@ -1672,7 +1692,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind loadBackgroundDrawablesIfNeeded(); mBackdropFrameRenderer.onResourcesLoaded( this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, - mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState)); + mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), + getCurrentColor(mNavigationColorViewState)); } mDecorCaptionView = createDecorCaptionView(inflater); @@ -1854,14 +1875,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } @Override - public void onWindowSizeIsChanging(Rect newBounds) { + public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets) { if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.setTargetRect(newBounds); + mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets); } } @Override - public void onWindowDragResizeStart(Rect initialBounds) { + public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, + Rect stableInsets) { if (mWindow.isDestroyed()) { // If the owner's window is gone, we should not be able to come here anymore. releaseThreadedRenderer(); @@ -1875,7 +1898,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind loadBackgroundDrawablesIfNeeded(); mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer, initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, - mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState)); + mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), + getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets, + stableInsets); // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. // If we want to get the shadow shown while resizing, we would need to elevate a new @@ -1971,10 +1996,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0; } - int getStatusBarHeight() { - return mStatusColorViewState.view != null ? mStatusColorViewState.view.getHeight() : 0; - } - /** * Converts a DIP measure into physical pixels. * @param dip The dip value. diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 3d5091a78687..bd01c73adf80 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -746,10 +746,12 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { if (mNeedsCopy) { SkPixelRef* recycledPixels = mRecycledBitmap->refPixelRef(); void* dst = recycledPixels->pixels(); - size_t dstRowBytes = mRecycledBitmap->rowBytes(); - size_t bytesToCopy = SkTMin(mRecycledBitmap->info().minRowBytes(), + const size_t dstRowBytes = mRecycledBitmap->rowBytes(); + const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(), mSkiaBitmap->info().minRowBytes()); - for (int y = 0; y < mRecycledBitmap->info().height(); y++) { + const int rowsToCopy = std::min(mRecycledBitmap->info().height(), + mSkiaBitmap->info().height()); + for (int y = 0; y < rowsToCopy; y++) { memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy); dst = SkTAddOffset<void>(dst, dstRowBytes); } diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 35b5016e1b07..cf73316d494b 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -510,7 +510,7 @@ public: size_t glyphCount = end - start; - if (CC_UNLIKELY(canvas->isHighContrastText())) { + if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { // high contrast draw path int color = paint.getColor(); int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp index e88287644555..7314fbcd92d2 100644 --- a/core/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp @@ -138,11 +138,6 @@ static jlong createGroup(JNIEnv*, jobject, jlong srcGroupPtr) { return reinterpret_cast<jlong>(newGroup); } -static void deleteNode(JNIEnv*, jobject, jlong nodePtr) { - VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr); - delete node; -} - static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) { VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr); const char* nodeName = env->GetStringUTFChars(nameStr, NULL); @@ -346,7 +341,6 @@ static const JNINativeMethod gMethods[] = { {"nCreateClipPath", "!(J)J", (void*)createClipPath}, {"nCreateGroup", "!()J", (void*)createEmptyGroup}, {"nCreateGroup", "!(J)J", (void*)createGroup}, - {"nDestroy", "!(J)V", (void*)deleteNode}, {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName}, {"nUpdateGroupProperties", "!(JFFFFFFF)V", (void*)updateGroupProperties}, diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 4842e1bfa10f..e39bb1cb1a5a 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -205,8 +205,8 @@ nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject s SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); Sensor const* const* sensorList; - size_t count = mgr->getSensorList(&sensorList); - if (size_t(index) >= count) { + ssize_t count = mgr->getSensorList(&sensorList); + if (ssize_t(index) >= count) { return false; } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index b1d4e2646ba4..a9003c1888d2 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW #include <EGL/egl.h> @@ -24,7 +25,10 @@ #include <android_runtime/AndroidRuntime.h> #include <Animator.h> +#include <DamageAccumulator.h> +#include <Matrix.h> #include <RenderNode.h> +#include <TreeInfo.h> #include <Paint.h> #include "core_jni_helpers.h" @@ -462,6 +466,69 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, } // ---------------------------------------------------------------------------- +// SurfaceView position callback +// ---------------------------------------------------------------------------- + +jmethodID gSurfaceViewPositionUpdateMethod; + +static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, + jlong renderNodePtr, jobject surfaceview) { + class SurfaceViewPositionUpdater : public RenderNode::PositionListener { + public: + SurfaceViewPositionUpdater(JNIEnv* env, jobject surfaceview) { + env->GetJavaVM(&mVm); + mWeakRef = env->NewWeakGlobalRef(surfaceview); + } + + virtual ~SurfaceViewPositionUpdater() { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } + + virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override { + if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return; + ATRACE_NAME("Update SurfaceView position"); + + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + return; + } + Matrix4 transform; + info.damageAccumulator->computeCurrentTransform(&transform); + const RenderProperties& props = node.properties(); + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); + transform.mapRect(bounds); + bounds.left -= info.windowInsetLeft; + bounds.right -= info.windowInsetLeft; + bounds.top -= info.windowInsetTop; + bounds.bottom -= info.windowInsetTop; + env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod, + (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top, + (jint) bounds.right, (jint) bounds.bottom); + env->DeleteLocalRef(localref); + } + + private: + JNIEnv* jnienv() { + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm); + } + return env; + } + + JavaVM* mVm; + jobject mWeakRef; + }; + + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->setPositionListener(new SurfaceViewPositionUpdater(env, surfaceview)); +} + +// ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -539,9 +606,14 @@ static const JNINativeMethod gMethods[] = { { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators }, + + { "nRequestPositionUpdates", "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates }, }; int register_android_view_RenderNode(JNIEnv* env) { + jclass clazz = FindClassOrDie(env, "android/view/SurfaceView"); + gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz, + "updateWindowPositionRT", "(JIIII)V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 8c907dd35a1e..acd0501362b5 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -134,7 +134,14 @@ public: virtual void prepareTree(TreeInfo& info) { info.errorHandler = this; + // TODO: This is hacky + info.windowInsetLeft = -stagingProperties().getLeft(); + info.windowInsetTop = -stagingProperties().getTop(); + info.updateWindowPositions = true; RenderNode::prepareTree(info); + info.updateWindowPositions = false; + info.windowInsetLeft = 0; + info.windowInsetTop = 0; info.errorHandler = NULL; } @@ -369,28 +376,28 @@ static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz, static void android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface); - proxy->initialize(window); + sp<Surface> surface = android_view_Surface_getSurface(env, jsurface); + proxy->initialize(surface); } static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - sp<ANativeWindow> window; + sp<Surface> surface; if (jsurface) { - window = android_view_Surface_getNativeWindow(env, jsurface); + surface = android_view_Surface_getSurface(env, jsurface); } - proxy->updateSurface(window); + proxy->updateSurface(surface); } static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - sp<ANativeWindow> window; + sp<Surface> surface; if (jsurface) { - window = android_view_Surface_getNativeWindow(env, jsurface); + surface = android_view_Surface_getSurface(env, jsurface); } - return proxy->pauseSurface(window); + return proxy->pauseSurface(surface); } static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cc489022345c..4cddb6c1bac4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2739,6 +2739,7 @@ android.service.notification.NotificationAssistantService}, to ensure that only the system can bind to it. <p>Protection level: signature + @hide This is not a third-party API (intended for system apps). --> --> <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE" android:protectionLevel="signature" /> diff --git a/core/res/res/drawable/ic_close.xml b/core/res/res/drawable/ic_close.xml new file mode 100644 index 000000000000..708695994ab6 --- /dev/null +++ b/core/res/res/drawable/ic_close.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z" + android:fillColor="#FF000000"/> +</vector> diff --git a/core/res/res/drawable/ic_feedback.xml b/core/res/res/drawable/ic_feedback.xml new file mode 100644 index 000000000000..b2d1cb80c9c1 --- /dev/null +++ b/core/res/res/drawable/ic_feedback.xml @@ -0,0 +1,27 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M0 0h24v24H0z" + android:fillColor="#00000000"/> + <path + android:fillColor="#FF000000" + android:pathData="M20.0,2.0L4.0,2.0c-1.1,0.0 -1.9,0.9 -1.99,2.0L2.0,22.0l4.0,-4.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L22.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,12.0l-2.0,0.0l0.0,-2.0l2.0,0.0l0.0,2.0zm0.0,-4.0l-2.0,0.0L11.0,6.0l2.0,0.0l0.0,4.0z"/> +</vector> diff --git a/core/res/res/drawable/ic_refresh.xml b/core/res/res/drawable/ic_refresh.xml new file mode 100644 index 000000000000..1f671684861f --- /dev/null +++ b/core/res/res/drawable/ic_refresh.xml @@ -0,0 +1,27 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/> + <path + android:pathData="M0 0h24v24H0z" + android:fillColor="#00000000"/> +</vector> diff --git a/core/res/res/drawable/ic_schedule.xml b/core/res/res/drawable/ic_schedule.xml new file mode 100644 index 000000000000..899dc821844e --- /dev/null +++ b/core/res/res/drawable/ic_schedule.xml @@ -0,0 +1,30 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M11.99,2.0C6.47,2.0 2.0,6.48 2.0,12.0s4.47,10.0 9.99,10.0C17.52,22.0 22.0,17.52 22.0,12.0S17.52,2.0 11.99,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.58 -8.0,-8.0s3.58,-8.0 8.0,-8.0 8.0,3.58 8.0,8.0 -3.58,8.0 -8.0,8.0z"/> + <path + android:pathData="M0 0h24v24H0z" + android:fillColor="#00000000"/> + <path + android:fillColor="#FF000000" + android:pathData="M12.5,7.0L11.0,7.0l0.0,6.0l5.25,3.1 0.75,-1.23 -4.5,-2.67z"/> +</vector> diff --git a/core/res/res/layout/app_anr_dialog.xml b/core/res/res/layout/app_anr_dialog.xml index e8169ee1bc86..8bef116fa89e 100644 --- a/core/res/res/layout/app_anr_dialog.xml +++ b/core/res/res/layout/app_anr_dialog.xml @@ -18,28 +18,31 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingTop="@dimen/dialog_list_padding_vertical_material" + android:paddingTop="@dimen/aerr_padding_list_top" android:paddingBottom="@dimen/dialog_list_padding_vertical_material"> - <TextView + <Button android:id="@+id/aerr_close" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_close_app" + android:drawableStart="@drawable/ic_close" style="@style/aerr_list_item" /> - <TextView + <Button android:id="@+id/aerr_wait" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_wait" + android:drawableStart="@drawable/ic_schedule" style="@style/aerr_list_item" /> - <TextView + <Button android:id="@+id/aerr_report" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_report" + android:drawableStart="@drawable/ic_feedback" style="@style/aerr_list_item" /> </LinearLayout> diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml index 46a2b2af1a28..824f97f6585a 100644 --- a/core/res/res/layout/app_error_dialog.xml +++ b/core/res/res/layout/app_error_dialog.xml @@ -21,9 +21,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingTop="@dimen/dialog_list_padding_vertical_material" - android:paddingBottom="@dimen/dialog_list_padding_vertical_material" -> + android:paddingTop="@dimen/aerr_padding_list_top" + android:paddingBottom="@dimen/dialog_list_padding_vertical_material"> <Button @@ -31,6 +30,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_restart" + android:drawableStart="@drawable/ic_refresh" style="@style/aerr_list_item" /> @@ -39,6 +39,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_reset" + android:drawableStart="@drawable/ic_refresh" style="@style/aerr_list_item" /> @@ -47,6 +48,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_report" + android:drawableStart="@drawable/ic_feedback" style="@style/aerr_list_item" /> @@ -55,6 +57,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_close" + android:drawableStart="@drawable/ic_close" style="@style/aerr_list_item" /> @@ -63,6 +66,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/aerr_mute" + android:drawableStart="@drawable/ic_close" style="@style/aerr_list_item" /> diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 0a7ac776744a..305c8b0a0c0f 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -25,6 +25,7 @@ android:gravity="center" android:paddingTop="8dp" android:paddingBottom="8dp" + android:focusable="true" android:background="?attr/selectableItemBackgroundBorderless"> <FrameLayout android:layout_width="wrap_content" diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index dd18544783a5..7399fa9d58ce 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -48,8 +48,6 @@ <color name="primary_text_default_material_light">#de000000</color> <!-- 54% black --> <color name="secondary_text_default_material_light">#8a000000</color> - <!-- 38% black --> - <color name="tertiary_text_default_material_light">#61000000</color> <!-- 100% white --> <color name="primary_text_default_material_dark">#ffffffff</color> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 7c6f3389c5d0..8e86f78e02fd 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -449,5 +449,7 @@ <item type="dimen" format="integer" name="time_picker_column_start_material">0</item> <item type="dimen" format="integer" name="time_picker_column_end_material">1</item> + <item type="dimen" name="aerr_padding_list_top">15dp</item> + <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index f0960c78d418..86b9f1d1b7d5 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1403,8 +1403,12 @@ please see styles_device_defaults.xml. <item name="textAppearance">?attr/textAppearanceListItemSmall</item> <item name="textColor">?attr/textColorAlertDialogListItem</item> <item name="gravity">center_vertical</item> - <item name="paddingStart">?attr/listPreferredItemPaddingStart</item> - <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item> + <item name="paddingStart">?attr/dialogPreferredPadding</item> + <item name="paddingEnd">?attr/dialogPreferredPadding</item> + <item name="background">?attr/selectableItemBackground</item> + <item name="drawablePadding">32dp</item> + <item name="drawableTint">@color/accent_material_light</item> + <item name="drawableTintMode">src_atop</item> </style> <!-- Wifi dialog styles --> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 0a52f41e3603..9efcfdace11b 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -444,7 +444,7 @@ please see styles_device_defaults.xml. </style> <style name="TextAppearance.Material.Notification.Info"> - <item name="textColor">@color/tertiary_text_default_material_light</item> + <item name="textColor">@color/secondary_text_default_material_light</item> <item name="textSize">@dimen/notification_subtext_size</item> </style> diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java index 19ce44a90815..3feb0e905f1a 100644 --- a/core/tests/coretests/src/android/print/BasePrintTest.java +++ b/core/tests/coretests/src/android/print/BasePrintTest.java @@ -141,7 +141,7 @@ public abstract class BasePrintTest extends InstrumentationTestCase { // Set to US locale. Resources resources = getInstrumentation().getTargetContext().getResources(); Configuration oldConfiguration = resources.getConfiguration(); - if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) { + if (!oldConfiguration.getLocales().get(0).equals(Locale.US)) { mOldLocale = oldConfiguration.getLocales(); DisplayMetrics displayMetrics = resources.getDisplayMetrics(); Configuration newConfiguration = new Configuration(oldConfiguration); diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index d2bd10613ded..de741b376d16 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -89,10 +89,7 @@ $(eval include $(BUILD_PREBUILT)) endef font_src_files := \ - Clockopia.ttf \ - AndroidClock.ttf \ - AndroidClock_Highlight.ttf \ - AndroidClock_Solid.ttf + AndroidClock.ttf $(foreach f, $(font_src_files), $(call build-one-font-module, $(f))) diff --git a/data/fonts/AndroidClock_Highlight.ttf b/data/fonts/AndroidClock_Highlight.ttf Binary files differdeleted file mode 100644 index 923bb30a2328..000000000000 --- a/data/fonts/AndroidClock_Highlight.ttf +++ /dev/null diff --git a/data/fonts/AndroidClock_Solid.ttf b/data/fonts/AndroidClock_Solid.ttf Binary files differdeleted file mode 100644 index 923bb30a2328..000000000000 --- a/data/fonts/AndroidClock_Solid.ttf +++ /dev/null diff --git a/data/fonts/Clockopia.ttf b/data/fonts/Clockopia.ttf Binary files differdeleted file mode 100644 index 3f7b6aaa8c08..000000000000 --- a/data/fonts/Clockopia.ttf +++ /dev/null diff --git a/data/fonts/MTLc3m.ttf b/data/fonts/MTLc3m.ttf Binary files differdeleted file mode 100644 index e9018f62825e..000000000000 --- a/data/fonts/MTLc3m.ttf +++ /dev/null diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf Binary files differdeleted file mode 100644 index 14f27d4f1847..000000000000 --- a/data/fonts/MTLmr3m.ttf +++ /dev/null diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 597a12245c11..acd785ede249 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -20,7 +20,4 @@ PRODUCT_COPY_FILES := \ PRODUCT_PACKAGES := \ DroidSansFallback.ttf \ DroidSansMono.ttf \ - Clockopia.ttf \ AndroidClock.ttf \ - AndroidClock_Highlight.ttf \ - AndroidClock_Solid.ttf \ diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 961d0ebec7e4..dc302c719af7 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -344,6 +344,9 @@ <family lang="und-Zsye"> <font weight="400" style="normal">NotoColorEmoji.ttf</font> </family> + <family> + <font weight="400" style="normal">DroidSansFallback.ttf</font> + </family> <!-- Tai Le and Mongolian are intentionally kept last, to make sure they don't override the East Asian punctuation for Chinese. diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index dfb8bb86f1e5..534121a2e361 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -459,7 +459,7 @@ public class Paint { // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV // ? HINTING_OFF : HINTING_ON); mCompatScaling = mInvCompatScaling = 1; - setTextLocales(LocaleList.getDefault()); + setTextLocales(LocaleList.getAdjustedDefault()); } /** @@ -500,7 +500,7 @@ public class Paint { mInvCompatScaling = 1; mBidiFlags = BIDI_DEFAULT_LTR; - setTextLocales(LocaleList.getDefault()); + setTextLocales(LocaleList.getAdjustedDefault()); setElegantTextHeight(false); mFontFeatureSettings = null; } @@ -1292,7 +1292,7 @@ public class Paint { */ @NonNull public Locale getTextLocale() { - return mLocales.getPrimary(); + return mLocales.get(0); } /** @@ -1317,7 +1317,7 @@ public class Paint { if (locale == null) { throw new IllegalArgumentException("locale cannot be null"); } - if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.getPrimary())) { + if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.get(0))) { return; } mLocales = new LocaleList(locale); @@ -1340,8 +1340,8 @@ public class Paint { * each language. * * By default, the text locale list is initialized to a one-member list just containing the - * system locale (as returned by {@link LocaleList#getDefault()}). This assumes that the text to - * be rendered will most likely be in the user's preferred language. + * system locales. This assumes that the text to be rendered will most likely be in the user's + * preferred language. * * If the actual language or languages of the text is/are known, then they can be provided to * the text renderer using this method. The text renderer may attempt to guess the diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 40dbe2770e01..0cde0b990456 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -335,6 +335,21 @@ public final class Rect implements Parcelable { } /** + * Insets the rectangle on all sides specified by the insets. + * @hide + * @param left The amount to add from the rectangle's left + * @param top The amount to add from the rectangle's top + * @param right The amount to subtract from the rectangle's right + * @param bottom The amount to subtract from the rectangle's bottom + */ + public void inset(int left, int top, int right, int bottom) { + this.left += left; + this.top += top; + this.right -= right; + this.bottom -= bottom; + } + + /** * Returns true if (x,y) is inside the rectangle. The left and top are * considered to be inside, while the right and bottom are not. This means * that for a x,y to be contained: left <= x < right and top <= y < bottom. diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 21ed7dd0099e..af8ccf5018b9 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -25,6 +25,8 @@ import android.animation.ValueAnimator; import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; +import android.app.Application; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; @@ -35,6 +37,7 @@ import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PorterDuff; import android.graphics.Rect; +import android.os.Build; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -200,6 +203,24 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { mMutated = false; } + /** + * In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable + * animations * for apps targeting N and later. For older apps, we ignore (i.e. quietly skip) + * these animations. + * + * @return whether invalid animations for vector drawable should be ignored. + */ + private static boolean shouldIgnoreInvalidAnimation() { + Application app = ActivityThread.currentApplication(); + if (app == null || app.getApplicationInfo() == null) { + return true; + } + if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { + return true; + } + return false; + } + @Override public ConstantState getConstantState() { mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations(); @@ -763,6 +784,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private boolean mInitialized = false; private boolean mAnimationPending = false; private boolean mIsReversible = false; + // This needs to be set before parsing starts. + private boolean mShouldIgnoreInvalidAnim; // TODO: Consider using NativeAllocationRegistery to track native allocation private final VirtualRefBasePtr mSetRefBasePtr; private WeakReference<RenderNode> mTarget = null; @@ -782,6 +805,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " + "re-initialized"); } + mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation(); parseAnimatorSet(set, 0); mInitialized = true; @@ -841,7 +865,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } else if (target instanceof VectorDrawable.VFullPath) { createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target, startTime); - } else { + } else if (!mShouldIgnoreInvalidAnim) { throw new IllegalArgumentException("ClipPath only supports PathData " + "property"); } @@ -850,10 +874,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } else if (target instanceof VectorDrawable.VectorDrawableState) { createRTAnimatorForRootGroup(values, animator, (VectorDrawable.VectorDrawableState) target, startTime); - } else { + } else if (!mShouldIgnoreInvalidAnim) { // Should never get here - throw new UnsupportedOperationException("Target should be group, path or vector. " + - target == null ? "Null target" : target.getClass() + " is not supported"); + throw new UnsupportedOperationException("Target should be either VGroup, VPath, " + + "or ConstantState, " + target == null ? "Null target" : target.getClass() + + " is not supported"); } } @@ -912,8 +937,12 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { long nativePtr = target.getNativePtr(); if (mTmpValues.type == Float.class || mTmpValues.type == float.class) { if (propertyId < 0) { - throw new IllegalArgumentException("Property: " + mTmpValues - .propertyName + " is not supported for FullPath"); + if (mShouldIgnoreInvalidAnim) { + return; + } else { + throw new IllegalArgumentException("Property: " + mTmpValues.propertyName + + " is not supported for FullPath"); + } } propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId, (Float) mTmpValues.startValue, (Float) mTmpValues.endValue); @@ -922,9 +951,13 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId, (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue); } else { - throw new UnsupportedOperationException("Unsupported type: " + - mTmpValues.type + ". Only float, int or PathData value is " + - "supported for Paths."); + if (mShouldIgnoreInvalidAnim) { + return; + } else { + throw new UnsupportedOperationException("Unsupported type: " + + mTmpValues.type + ". Only float, int or PathData value is " + + "supported for Paths."); + } } if (mTmpValues.dataSource != null) { float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator @@ -939,8 +972,12 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { long startTime) { long nativePtr = target.getNativeRenderer(); if (!animator.getPropertyName().equals("alpha")) { - throw new UnsupportedOperationException("Only alpha is supported for root " + - "group"); + if (mShouldIgnoreInvalidAnim) { + return; + } else { + throw new UnsupportedOperationException("Only alpha is supported for root " + + "group"); + } } Float startValue = null; Float endValue = null; @@ -953,7 +990,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } } if (startValue == null && endValue == null) { - throw new UnsupportedOperationException("No alpha values are specified"); + if (mShouldIgnoreInvalidAnim) { + return; + } else { + throw new UnsupportedOperationException("No alpha values are specified"); + } } long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue); createNativeChildAnimator(propertyPtr, startTime, animator); diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index f4bbc8c43d08..bdbf3c04b000 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -924,8 +924,11 @@ public class VectorDrawable extends Drawable { private int mChangingConfigurations; private int[] mThemeAttrs; private String mGroupName = null; - private long mNativePtr = 0; + // The native object will be created in the constructor and will be destroyed in native + // when the neither java nor native has ref to the tree. This pointer should be valid + // throughout this VGroup Java object's life. + private final long mNativePtr; public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) { mIsStateful = copy.mIsStateful; @@ -1065,16 +1068,6 @@ public class VectorDrawable extends Drawable { } @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nDestroy(mNativePtr); - mNativePtr = 0; - } - super.finalize(); - } - - - @Override public void applyTheme(Theme t) { if (mThemeAttrs != null) { final TypedArray a = t.resolveAttributes(mThemeAttrs, @@ -1208,10 +1201,10 @@ public class VectorDrawable extends Drawable { * Clip path, which only has name and pathData. */ private static class VClipPath extends VPath { - long mNativePtr = 0; + private final long mNativePtr; + public VClipPath() { mNativePtr = nCreateClipPath(); - // Empty constructor. } public VClipPath(VClipPath copy) { @@ -1225,14 +1218,6 @@ public class VectorDrawable extends Drawable { } @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nDestroy(mNativePtr); - mNativePtr = 0; - } - super.finalize(); - } - @Override public void inflate(Resources r, AttributeSet attrs, Theme theme) { final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawableClipPath); @@ -1317,10 +1302,9 @@ public class VectorDrawable extends Drawable { ComplexColor mStrokeColors = null; ComplexColor mFillColors = null; - private long mNativePtr = 0; + private final long mNativePtr; public VFullPath() { - // Empty constructor. mNativePtr = nCreateFullPath(); } @@ -1384,15 +1368,6 @@ public class VectorDrawable extends Drawable { a.recycle(); } - @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nDestroy(mNativePtr); - mNativePtr = 0; - } - super.finalize(); - } - private void updateStateFromTypedArray(TypedArray a) { int byteCount = TOTAL_PROPERTY_COUNT * 4; if (mPropertyData == null) { @@ -1647,7 +1622,7 @@ public class VectorDrawable extends Drawable { private static native void nDraw(long rendererPtr, long canvasWrapperPtr, long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache); private static native long nCreateFullPath(); - private static native long nCreateFullPath(long mNativeFullPathPtr); + private static native long nCreateFullPath(long nativeFullPathPtr); private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties, int length); @@ -1663,7 +1638,6 @@ public class VectorDrawable extends Drawable { private static native long nCreateGroup(); private static native long nCreateGroup(long groupPtr); - private static native void nDestroy(long nodePtr); private static native void nSetName(long nodePtr, String name); private static native boolean nGetGroupProperties(long groupPtr, float[] properties, int length); diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java index 7cf4b04cc996..d72688079d0e 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -269,7 +269,7 @@ public class KeyInfo implements KeySpec { /** * Returns {@code true} if the requirement that this key can only be used if the user has been - * authenticated if enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or + * authenticated is enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or * Secure Element (SE)). * * @see #isUserAuthenticationRequired() diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index c232bd1101a3..7b43947bc1e0 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -252,7 +252,8 @@ LOCAL_SRC_FILES += \ tests/unit/SkiaBehaviorTests.cpp \ tests/unit/StringUtilsTests.cpp \ tests/unit/TextDropShadowCacheTests.cpp \ - tests/unit/VectorDrawableTests.cpp + tests/unit/VectorDrawableTests.cpp \ + tests/unit/GradientCacheTests.cpp ifeq (true, $(HWUI_NEW_OPS)) LOCAL_SRC_FILES += \ diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index e44fc20feaa8..250296ecc89f 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -57,7 +57,7 @@ public: // Returns the current dirty area, *NOT* transformed by pushed transforms void peekAtDirty(SkRect* dest) const; - void computeCurrentTransform(Matrix4* outMatrix) const; + ANDROID_API void computeCurrentTransform(Matrix4* outMatrix) const; void finish(SkRect* totalDirty); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 044c7cb65718..11293d61211b 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -153,10 +153,13 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, texture->blend = info.hasAlpha; texture->generation = 1; - // Asume the cache is always big enough + // Assume the cache is always big enough const uint32_t size = info.width * 2 * bytesPerPixel(); while (getSize() + size > mMaxSize) { - mCache.removeOldest(); + LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(), + "Ran out of things to remove from the cache? getSize() = %" PRIu32 + ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32, + getSize(), size, mMaxSize, info.width); } generateTexture(colors, positions, info.width, 2, texture); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d4588edea207..bade216b3b21 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -381,6 +381,10 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( willHaveFunctor, functorsNeedLayer); + if (CC_UNLIKELY(mPositionListener.get())) { + mPositionListener->onPositionUpdated(*this, info); + } + prepareLayer(info, animatorDirtyMask); if (info.mode == TreeInfo::MODE_FULL) { pushStagingDisplayListChanges(info); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 8e4a3df271f5..f248de54acba 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -209,6 +209,19 @@ public: OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh... #endif + class ANDROID_API PositionListener { + public: + virtual ~PositionListener() {} + virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) = 0; + }; + + // Note this is not thread safe, this needs to be called + // before the RenderNode is used for drawing. + // RenderNode takes ownership of the pointer + ANDROID_API void setPositionListener(PositionListener* listener) { + mPositionListener.reset(listener); + } + private: typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair; @@ -317,6 +330,8 @@ private: // This is *NOT* thread-safe, and should therefore only be tracking // mDisplayList, not mStagingDisplayList. uint32_t mParentCount; + + std::unique_ptr<PositionListener> mPositionListener; }; // class RenderNode } /* namespace uirenderer */ diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index be25516c587a..accd3038cb9c 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -86,6 +86,12 @@ public: #endif ErrorHandler* errorHandler = nullptr; + // Frame number for use with synchronized surfaceview position updating + int64_t frameNumber = -1; + int32_t windowInsetLeft = 0; + int32_t windowInsetTop = 0; + bool updateWindowPositions = false; + struct Out { bool hasFunctors = false; // This is only updated if evaluateAnimations is true diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 541c799f0149..2e3856fafb60 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -324,7 +324,7 @@ void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scale // Save the current clip information, which is local to this group. outCanvas->save(); // Draw the group tree in the same order as the XML file. - for (Node* child : mChildren) { + for (auto& child : mChildren) { child->draw(outCanvas, stackedMatrix, scaleX, scaleY); } // Restore the previous clip information. @@ -361,7 +361,7 @@ void Group::getLocalMatrix(SkMatrix* outMatrix) { } void Group::addChild(Node* child) { - mChildren.push_back(child); + mChildren.emplace_back(child); } bool Group::getProperties(float* outProperties, int length) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index f8f1ea62a624..36a8aebeaa33 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -316,7 +316,7 @@ private: // Count of the properties, must be at the end. Count, }; - std::vector<Node*> mChildren; + std::vector< std::unique_ptr<Node> > mChildren; Properties mProperties; }; @@ -360,7 +360,7 @@ private: float mViewportHeight = 0; float mRootAlpha = 1.0f; - Group* mRootNode; + std::unique_ptr<Group> mRootNode; SkRect mBounds; SkMatrix mCanvasMatrix; SkPaint mPaint; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d4116218e5e1..ea702c01694e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -92,18 +92,18 @@ void CanvasContext::destroy() { } } -void CanvasContext::setSurface(ANativeWindow* window) { +void CanvasContext::setSurface(Surface* surface) { ATRACE_CALL(); - mNativeWindow = window; + mNativeSurface = surface; if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } - if (window) { - mEglSurface = mEglManager.createSurface(window); + if (surface) { + mEglSurface = mEglManager.createSurface(surface); } if (mEglSurface != EGL_NO_SURFACE) { @@ -127,8 +127,8 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { mSwapBehavior = swapBehavior; } -void CanvasContext::initialize(ANativeWindow* window) { - setSurface(window); +void CanvasContext::initialize(Surface* surface) { + setSurface(surface); #if !HWUI_NEW_OPS if (mCanvas) return; mCanvas = new OpenGLRenderer(mRenderThread.renderState()); @@ -136,11 +136,11 @@ void CanvasContext::initialize(ANativeWindow* window) { #endif } -void CanvasContext::updateSurface(ANativeWindow* window) { - setSurface(window); +void CanvasContext::updateSurface(Surface* surface) { + setSurface(surface); } -bool CanvasContext::pauseSurface(ANativeWindow* window) { +bool CanvasContext::pauseSurface(Surface* surface) { return mRenderThread.removeFrameCallback(this); } @@ -204,6 +204,10 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, info.renderer = mCanvas; #endif + if (CC_LIKELY(mNativeSurface.get())) { + info.frameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber()); + } + mAnimationContext->startFrame(info.mode); for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in @@ -219,7 +223,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, freePrefetechedLayers(); GL_CHECKPOINT(MODERATE); - if (CC_UNLIKELY(!mNativeWindow.get())) { + if (CC_UNLIKELY(!mNativeSurface.get())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); info.out.canDrawThisFrame = false; return; @@ -242,8 +246,9 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, } else { // We're maybe behind? Find out for sure int runningBehind = 0; - mNativeWindow->query(mNativeWindow.get(), - NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); + // TODO: Have this method be on Surface, too, not just ANativeWindow... + ANativeWindow* window = mNativeSurface.get(); + window->query(window, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); info.out.canDrawThisFrame = !runningBehind; } } else { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 63a7977f0cc9..168166ef5b23 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -39,6 +39,7 @@ #include <SkBitmap.h> #include <SkRect.h> #include <utils/Functor.h> +#include <gui/Surface.h> #include <set> #include <string> @@ -75,10 +76,10 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void initialize(ANativeWindow* window); - void updateSurface(ANativeWindow* window); - bool pauseSurface(ANativeWindow* window); - bool hasSurface() { return mNativeWindow.get(); } + void initialize(Surface* surface); + void updateSurface(Surface* surface); + bool pauseSurface(Surface* surface); + bool hasSurface() { return mNativeSurface.get(); } void setup(int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); @@ -172,7 +173,7 @@ private: // lifecycle tracking friend class android::uirenderer::RenderState; - void setSurface(ANativeWindow* window); + void setSurface(Surface* window); void requireSurface(); void freePrefetechedLayers(); @@ -182,7 +183,7 @@ private: RenderThread& mRenderThread; EglManager& mEglManager; - sp<ANativeWindow> mNativeWindow; + sp<Surface> mNativeSurface; EGLSurface mEglSurface = EGL_NO_SURFACE; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 466fef9def09..364d4dda1e7f 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -228,6 +228,13 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window) { LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", (void*) window, egl_error_str()); + + if (mSwapBehavior != SwapBehavior::Preserved) { + LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE, + "Failed to set swap behavior to destroyed for window %p, eglErr = %s", + (void*) window, egl_error_str()); + } + return surface; } @@ -337,8 +344,8 @@ bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { // For some reason our surface was destroyed out from under us // This really shouldn't happen, but if it does we can recover easily // by just not trying to use the surface anymore - ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...", - frame.mSurface); + ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...", + err, frame.mSurface); return false; } LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering", diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 1d1b144bd47e..7c6cd7e014ef 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -139,38 +139,38 @@ void RenderProxy::setName(const char* name) { postAndWait(task); // block since name/value pointers owned by caller } -CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) { - args->context->initialize(args->window); +CREATE_BRIDGE2(initialize, CanvasContext* context, Surface* surface) { + args->context->initialize(args->surface); return nullptr; } -void RenderProxy::initialize(const sp<ANativeWindow>& window) { +void RenderProxy::initialize(const sp<Surface>& surface) { SETUP_TASK(initialize); args->context = mContext; - args->window = window.get(); + args->surface = surface.get(); post(task); } -CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) { - args->context->updateSurface(args->window); +CREATE_BRIDGE2(updateSurface, CanvasContext* context, Surface* surface) { + args->context->updateSurface(args->surface); return nullptr; } -void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { +void RenderProxy::updateSurface(const sp<Surface>& surface) { SETUP_TASK(updateSurface); args->context = mContext; - args->window = window.get(); + args->surface = surface.get(); postAndWait(task); } -CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) { - return (void*) args->context->pauseSurface(args->window); +CREATE_BRIDGE2(pauseSurface, CanvasContext* context, Surface* surface) { + return (void*) args->context->pauseSurface(args->surface); } -bool RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { +bool RenderProxy::pauseSurface(const sp<Surface>& surface) { SETUP_TASK(pauseSurface); args->context = mContext; - args->window = window.get(); + args->surface = surface.get(); return (bool) postAndWait(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 4180d8020179..178724a85d04 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -67,9 +67,9 @@ public: ANDROID_API bool loadSystemProperties(); ANDROID_API void setName(const char* name); - ANDROID_API void initialize(const sp<ANativeWindow>& window); - ANDROID_API void updateSurface(const sp<ANativeWindow>& window); - ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window); + ANDROID_API void initialize(const sp<Surface>& surface); + ANDROID_API void updateSurface(const sp<Surface>& surface); + ANDROID_API bool pauseSurface(const sp<Surface>& surface); ANDROID_API void setup(int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setLightCenter(const Vector3& lightCenter); diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp new file mode 100644 index 000000000000..5ee17054183c --- /dev/null +++ b/libs/hwui/tests/unit/GradientCacheTests.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "Extensions.h" +#include "GradientCache.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; + +RENDERTHREAD_TEST(GradientCache, addRemove) { + Extensions extensions; + GradientCache cache(extensions); + cache.setMaxSize(5000); + + SkColor colors[] = { 0xFF00FF00, 0xFFFF0000, 0xFF0000FF }; + float positions[] = { 1, 2, 3 }; + Texture* texture = cache.get(colors, positions, 3); + ASSERT_TRUE(texture); + ASSERT_FALSE(texture->cleanup); + ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize()); + ASSERT_TRUE(cache.getSize()); + cache.clear(); + ASSERT_EQ(cache.getSize(), 0u); +} diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 3007d86afebc..b26b310a904b 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1784,9 +1784,9 @@ public class AudioTrack implements AudioRouting * Note that the actual playback of this data might occur after this function returns. * * @param audioData the array that holds the data to play. - * @param offsetInBytes the offset expressed in bytes in audioData where the data to play + * @param offsetInBytes the offset expressed in bytes in audioData where the data to write * starts. - * @param sizeInBytes the number of bytes to read in audioData after the offset. + * @param sizeInBytes the number of bytes to write in audioData after the offset. * @return zero or the positive number of bytes that were written, or * {@link #ERROR_INVALID_OPERATION} * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if @@ -1821,9 +1821,9 @@ public class AudioTrack implements AudioRouting * Note that the actual playback of this data might occur after this function returns. * * @param audioData the array that holds the data to play. - * @param offsetInBytes the offset expressed in bytes in audioData where the data to play + * @param offsetInBytes the offset expressed in bytes in audioData where the data to write * starts. - * @param sizeInBytes the number of bytes to read in audioData after the offset. + * @param sizeInBytes the number of bytes to write in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written @@ -1920,8 +1920,8 @@ public class AudioTrack implements AudioRouting * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function returns. * - * @param audioData the array that holds the data to play. - * @param offsetInShorts the offset expressed in shorts in audioData where the data to play + * @param audioData the array that holds the data to write. + * @param offsetInShorts the offset expressed in shorts in audioData where the data to write * starts. * @param sizeInShorts the number of shorts to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no @@ -1987,7 +1987,7 @@ public class AudioTrack implements AudioRouting * and the write mode is ignored. * Note that the actual playback of this data might occur after this function returns. * - * @param audioData the array that holds the data to play. + * @param audioData the array that holds the data to write. * The implementation does not clip for sample values within the nominal range * [-1.0f, 1.0f], provided that all gains in the audio pipeline are * less than or equal to unity (1.0f), and in the absence of post-processing effects @@ -1998,8 +1998,8 @@ public class AudioTrack implements AudioRouting * and later processing in the audio path. Therefore applications are encouraged * to provide samples values within the nominal range. * @param offsetInFloats the offset, expressed as a number of floats, - * in audioData where the data to play starts. - * @param sizeInFloats the number of floats to read in audioData after the offset. + * in audioData where the data to write starts. + * @param sizeInFloats the number of floats to write in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written @@ -2070,7 +2070,7 @@ public class AudioTrack implements AudioRouting * and the write mode is ignored. * Note that the actual playback of this data might occur after this function returns. * - * @param audioData the buffer that holds the data to play, starting at the position reported + * @param audioData the buffer that holds the data to write, starting at the position reported * by <code>audioData.position()</code>. * <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will * have been advanced to reflect the amount of data that was successfully written to @@ -2137,7 +2137,7 @@ public class AudioTrack implements AudioRouting /** * Writes the audio data to the audio sink for playback in streaming mode on a HW_AV_SYNC track. * The blocking behavior will depend on the write mode. - * @param audioData the buffer that holds the data to play, starting at the position reported + * @param audioData the buffer that holds the data to write, starting at the position reported * by <code>audioData.position()</code>. * <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will * have been advanced to reflect the amount of data that was successfully written to diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java index f9fdd8daa89a..54543ec43f89 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -428,8 +428,17 @@ public class AudioMixingRule { } } // rule didn't exist, add it - // FIXME doesn't work with RULE_MATCH_UID yet - mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule)); + switch (match_rule) { + case RULE_MATCH_ATTRIBUTE_USAGE: + case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: + mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule)); + break; + case RULE_MATCH_UID: + mCriteria.add(new AttributeMatchCriterion(intProp, rule)); + break; + default: + throw new IllegalStateException("Unreachable code in addRuleInternal()"); + } } return this; } diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 80b3ffc23e31..56b25144d948 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -117,6 +117,9 @@ public final class MediaBrowser { * to the media browse service when connecting and retrieving the root id * for browsing, or null if none. The contents of this bundle may affect * the information returned when browsing. + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED */ public MediaBrowser(Context context, ComponentName serviceComponent, ConnectionCallback callback, Bundle rootHints) { diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 0393c943784d..3372524d138c 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -351,6 +351,9 @@ public abstract class MediaBrowserService extends Service { * root id for browsing, or null if none. The contents of this * bundle may affect the information returned when browsing. * @return The {@link BrowserRoot} for accessing this app's content or null. + * @see BrowserRoot#EXTRA_RECENT + * @see BrowserRoot#EXTRA_OFFLINE + * @see BrowserRoot#EXTRA_SUGGESTED */ public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints); @@ -667,6 +670,57 @@ public abstract class MediaBrowserService extends Service { * when first connected. */ public static final class BrowserRoot { + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for recently played media items. + * + * <p>When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving media items that are recently played. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + * <p>The root hint may contain multiple keys. + * + * @see #EXTRA_OFFLINE + * @see #EXTRA_SUGGESTED + */ + public static final String EXTRA_RECENT = "android.service.media.extra.RECENT"; + + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for offline media items. + * + * <p>When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving media items that are can be played without an + * internet connection. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + * <p>The root hint may contain multiple keys. + * + * @see #EXTRA_RECENT + * @see #EXTRA_SUGGESTED + */ + public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; + + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for suggested media items. + * + * <p>When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving the media items suggested by the media browser + * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)} + * is considered ordered by relevance, first being the top suggestion. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + * <p>The root hint may contain multiple keys. + * + * @see #EXTRA_RECENT + * @see #EXTRA_OFFLINE + */ + public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; + final private String mRootId; final private Bundle mExtras; diff --git a/packages/DocumentsUI/res/drawable/cabinet.png b/packages/DocumentsUI/res/drawable/cabinet.png Binary files differdeleted file mode 100644 index da440239cd0f..000000000000 --- a/packages/DocumentsUI/res/drawable/cabinet.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable/cabinet.xml b/packages/DocumentsUI/res/drawable/cabinet.xml new file mode 100644 index 000000000000..843ffc74e593 --- /dev/null +++ b/packages/DocumentsUI/res/drawable/cabinet.xml @@ -0,0 +1,81 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="672dp" + android:height="921dp" + android:viewportWidth="672.0" + android:viewportHeight="921.0"> + <path + android:pathData="M286,0c5,0,10,0,15,0c0.1,1.8,1.5,1.8,2.8,2.1c11.1,2,22.1,4,33.2,6.1c31.8,6.1,63.7,12.3,95.5,18.5 c16.1,3.1,32.1,6.2,48.2,9.3c26,4.9,52.1,9.3,78,14.6c10.8,2.2,21.6,4.6,32.3,6.5c11.3,2,22.6,4.7,34,6c7.9,0.9,7.9,1.1,7.9,9.2 c0,237.3,0,474.5,0,711.8c-1.5,0.9,-3,2,-4.6,2.8c-18.3,8.3,-36.6,16.6,-54.8,25c-29.3,13.4,-58.5,26.8,-87.8,40.3 c-23.5,10.9,-47,21.8,-70.4,32.8c-2.1,1,-4.2,1.5,-6.3,1.1c-6.8,-1.3,-13.6,-2.5,-20.1,-4.9c5.9,-0.3,11.4,1.9,17.1,2.9c5.9,1.1,5.9,1,5.9,-4.9 c0,-17.1,0.1,-34.3,0,-51.4c-0.3,-68.9,-0.7,-137.8,-1,-206.7c0,-35.8,0,-71.6,0.1,-107.4c0,-3.8,-0.6,-5.2,-4.7,-3.7c-7.9,2.9,-16,5.4,-24.1,7.8 c-14.1,4.3,-27.8,10,-42.2,13.2c0,-64,0,-127.9,-0.1,-191.9c0,-4.1,1.3,-5.9,5.1,-7c21,-6.6,42,-13.4,63,-20.2c2.3,-0.8,4.4,-1.8,4,-4.9 c0,-59.3,0,-118.7,0,-178c0,-1.3,-0.7,-2,-2,-2c-2.6,-0.4,-5.2,-0.7,-7.8,-1.2c-30.2,-5.3,-60.5,-10.6,-90.7,-16c-31.9,-5.6,-63.7,-11.3,-95.6,-16.9 c-24.9,-4.4,-49.8,-8.7,-74.6,-13.1C117.1,75.6,93.1,71.3,69,67c-0.3,-0.3,-0.7,-0.7,-1,-1c17.4,-5.3,34.8,-10.7,52.3,-15.9 c29.4,-8.7,58.8,-17.2,88.2,-25.8c24.3,-7.1,48.6,-14.2,72.9,-21.4C283.1,2.4,285.6,2.7,286,0z" + android:fillColor="#EFEFEE"/> + <path + android:pathData="M412,307c0.4,3,-1.7,4.1,-4,4.9c-21,6.8,-42,13.6,-63,20.2c-3.8,1.2,-5.1,3,-5.1,7C340,403.1,340,467,340,531 c-11.8,-1.2,-23.3,-4.5,-34.9,-6.5c-10,-1.7,-19.9,-4.6,-30.1,-5.5c-0.7,-0.3,-1.4,-0.9,-2.2,-1c-19.8,-4,-39.5,-8,-59.3,-12c-12.2,-2.4,-24.3,-4.7,-36.5,-7 c-0.9,-0.3,-1.8,-0.8,-2.8,-1c-24.5,-4.9,-48.9,-9.9,-73.5,-14.6C89.3,481.3,78,477.1,66,478c-0.7,-1.6,-2.1,-1.8,-3.6,-2.1 c-11.1,-2.2,-22.2,-4.7,-33.3,-6.7c-9.7,-1.7,-19.1,-4.9,-29.1,-5.3c0,-64.3,0,-128.7,0,-193c0.8,-0.2,1.6,-0.4,2.4,-0.7c19,-7.8,37.9,-15.9,57.1,-23.4 c5.4,-2.1,6.7,-4.8,6.6,-10.2c-0.2,-55.1,-0.1,-110.2,-0.1,-165.4c0,-1.9,-1.4,-4.6,1.9,-5.4c0.3,0.3,0.7,0.7,1,1c-1.3,4.9,-1,9.9,-1,14.9 c0,51.1,0,102.3,0,153.4c0,1.2,0,2.3,0,3.5c0.1,3.5,1.2,5.9,5.3,6.5c7,1.1,14,2.6,21,3.9c22.1,4.3,44.1,8.6,66.2,12.8 c27.3,5.2,54.6,10.1,81.9,15.3c21.8,4.1,43.5,8.5,65.2,12.6c28.1,5.4,56.2,10.8,84.3,15.8C398.4,306.8,405.1,310.5,412,307z M105,329c0,3.3,0,6.7,-0.1,10c-0.1,2.5,0.4,3.6,3.4,4.2c30,5.3,59.9,10.9,89.8,16.5c3.4,0.6,5.1,0.2,4.9,-3.7 c-0.2,-3.3,-0.1,-6.7,-0.1,-10c0.5,-3.6,-0.1,-6.3,-4.7,-6.1c-1.1,0.1,-2.2,-0.6,-3.4,-0.8c-28.3,-5,-56.6,-9.9,-84.9,-14.9 C105.6,323.4,104.5,325.2,105,329z M65.9,280.8c13.7,2.5,27.4,4.9,41.1,7.4c32.6,5.9,65.2,11.8,97.8,17.8 c41.4,7.6,82.8,15.2,124.2,22.8c6.8,1.2,13.3,1.4,20,-1.3c9.3,-3.6,18.9,-6.3,28.4,-9.4c6.4,-2.1,12.8,-4.2,19.2,-6.4 c-4.2,-2,-8.3,-3,-12.4,-3.8c-22.6,-4.2,-45.3,-8,-67.9,-12.6c-14.6,-3,-29.2,-5.6,-43.8,-8.5c-24,-4.6,-48,-9.2,-72,-13.7c-15.9,-3,-31.8,-6.2,-47.8,-9.2 c-19,-3.6,-38,-7.3,-57,-10.6c-9.9,-1.7,-19.6,-4.5,-29.7,-5.2C48,255.6,30.1,263,12.2,270.4c0,0.4,0,0.7,0,1.1 C30.1,274.6,48,277.7,65.9,280.8z" + android:fillColor="#EAEAEA"/> + <path + android:pathData="M672,782c-6,0.9,-11.1,4.3,-16.4,6.9c-30.8,15,-61.5,30.3,-92.3,45.4c-34.8,17.1,-69.5,34.3,-104.5,51.1 c-13,6.3,-26,12.8,-39,19.1c-1.5,0.7,-3.7,1,-3.9,3.4c-3.7,0,-7.3,0,-11,0c-0.4,-3,-3.1,-2.3,-4.7,-2.7c-19.3,-4.8,-38.6,-9.5,-57.9,-14.1 c-27.5,-6.5,-55.2,-12.7,-82.6,-19.4c-30.9,-7.5,-61.8,-15,-92.7,-22.1c-24.8,-5.8,-49.5,-12,-74.3,-18C70.8,826.5,48.9,821.3,27,816 c-1.1,-0.3,-2.3,-0.5,-3.3,-1c-3.2,-1.3,-3.5,-3.3,-0.7,-5.4c0.9,-0.7,2,-1.2,3.1,-1.7c12,-5.3,24,-10.7,36,-16c0.4,-0.2,0.9,-0.2,1.9,-0.3 c0,3.6,0,7,0,10.5c0,1.6,-0.5,3.5,2,3.9c0.7,2.8,3.2,2.5,5.2,3c39.3,9,78.7,18.1,118.1,27c43.9,10,87.7,20,131.6,29.9 c9.7,2.2,19.2,4.9,29.1,6c1.1,1.5,2.6,0.9,4,1l0,0c4.1,2,8.5,2.6,13,3l0,0c7.2,2.4,14.4,4.3,22,5l0,0c6.5,2.5,13.3,3.6,20.1,4.9 c2.1,0.4,4.2,-0.1,6.3,-1.1c23.5,-11,46.9,-21.9,70.4,-32.8c29.2,-13.5,58.5,-26.9,87.8,-40.3c18.3,-8.4,36.6,-16.6,54.8,-25 c1.6,-0.7,3.1,-1.9,4.6,-2.8c2,-2.9,1.1,-6.2,0.9,-9.2c-0.3,-4.7,1.9,-5.5,5.7,-4.7c10.8,2.2,21.6,4.6,32.5,6.9C672,778.7,672,780.3,672,782z " + android:fillColor="#E6E4E4"/> + <path + android:pathData="M350,872c-9.9,-1.1,-19.4,-3.9,-29.1,-6C277,856,233.1,846,189.2,836c-39.4,-9,-78.7,-18,-118.1,-27 c-2,-0.4,-4.5,-0.2,-5.2,-3c0,-85.7,0,-171.4,0.1,-257.1c6.5,0.1,12.7,2.3,19,3.6c26.4,5.4,52.8,10.9,79.2,16.5c25.9,5.4,51.8,11,77.7,16.4 c26.2,5.5,52.5,11,78.7,16.5c30.1,6.3,60.2,12.6,90.3,19c0.3,68.9,0.7,137.8,1,206.7c0.1,17.1,0,34.3,0,51.4c0,5.9,0,6,-5.9,4.9 c-5.7,-1.1,-11.2,-3.2,-17.1,-2.9c0,0,0,0,0,0c-7,-3.1,-14.2,-5.4,-22,-5c0,0,0,0,0,0c-3.9,-2.9,-8.4,-2.9,-13,-3c0,0,0,0,0,0 C352.9,871.5,351.4,872.1,350,872z M177,687c0,3.2,0.1,6.3,0,9.5c-0.1,2.7,0.7,4,3.8,4.6c29.7,5.8,59.3,11.7,89,17.7 c2.4,0.5,4.7,0.1,4.9,-2.6c0.4,-3.7,1.2,-7.6,-0.6,-11.2c1,-3,1.2,-5.3,-3,-6c-29.6,-5.4,-59.2,-10.8,-88.7,-16.5C177.7,681.6,176.5,682.8,177,687 z" + android:fillColor="#E5E5E5"/> + <path + android:pathData="M411,621c-30.1,-6.3,-60.2,-12.6,-90.3,-19c-26.2,-5.5,-52.5,-11,-78.7,-16.5c-25.9,-5.5,-51.8,-11,-77.7,-16.4 c-26.4,-5.5,-52.8,-11.1,-79.2,-16.5c-6.3,-1.3,-12.5,-3.5,-19,-3.6c0,-23.6,0,-47.3,0,-70.9c12,-0.9,23.2,3.3,34.7,5.5c24.5,4.6,49,9.7,73.5,14.6 c1,0.2,1.9,0.6,2.8,1c0,3.3,0.7,6.7,0.7,9.9c0,5.6,2.4,7.5,7.5,8.4c15.3,2.7,30.5,5.8,45.8,8.7c12,2.3,24,4.4,36.1,6.6 c1.9,0.3,4.8,1.5,4.7,-1.4c-0.2,-4.6,1.7,-8.2,3.3,-12.1c10.2,0.9,20,3.8,30.1,5.5c11.7,2,23.1,5.3,34.9,6.5 c14.5,-3.2,28.1,-8.9,42.2,-13.2c8.1,-2.5,16.2,-5,24.1,-7.8c4.1,-1.5,4.8,-0.1,4.7,3.7C411,549.4,411,585.2,411,621z" + android:fillColor="#D9D9D9"/> + <path + android:pathData="M412,307c-6.9,3.5,-13.6,-0.2,-20.1,-1.3c-28.2,-5,-56.2,-10.4,-84.3,-15.8c-21.8,-4.1,-43.5,-8.5,-65.2,-12.6 c-27.3,-5.2,-54.6,-10.1,-81.9,-15.3c-22.1,-4.2,-44.1,-8.5,-66.2,-12.8c-7,-1.3,-13.9,-2.9,-21,-3.9c-4.1,-0.6,-5.2,-3,-5.3,-6.5c0,-1.2,0,-2.3,0,-3.5 c0,-51.1,0,-102.3,0,-153.4c0,-5,-0.3,-10,1,-14.9c24.1,4.3,48.1,8.6,72.2,12.8c24.9,4.4,49.8,8.7,74.6,13.1c31.9,5.6,63.7,11.3,95.6,16.9 c30.2,5.3,60.5,10.6,90.7,16c2.6,0.5,5.2,0.8,7.8,1.2c0,1.3,0.7,2,2,2C412,188.3,412,247.7,412,307z M409,217.4c0,-25.5,0,-51,0,-76.5 c0,-10.9,0.1,-11.2,-10.7,-13.2c-23.4,-4.4,-46.8,-8.5,-70.3,-12.6c-24.1,-4.3,-48.2,-8.4,-72.3,-12.6c-17.7,-3.1,-35.5,-6.3,-53.2,-9.4 c-22.1,-3.9,-44.3,-7.6,-66.4,-11.5c-20,-3.5,-40,-7.1,-60.1,-10.5c-6,-1,-6.1,-0.8,-6.1,5.6c0,53,0,105.9,0,158.9c0,1,0,2,0,3 c0.2,2.6,1,4.1,4,4.6c10.1,1.7,20.1,3.9,30.2,5.8c27.3,5.1,54.6,10.1,81.9,15.2c22.1,4.2,44.1,8.6,66.2,12.8 c27.3,5.2,54.6,10.2,81.9,15.3c22.7,4.3,45.5,8.6,68.2,12.8c6.5,1.2,6.5,1.1,6.5,-5.7C409,272,409,244.7,409,217.4z" + android:fillColor="#E8E8E8"/> + <path + android:pathData="M412,129c-1.3,0,-2,-0.7,-2,-2C411.3,127,412,127.7,412,129z" + android:fillColor="#EAEAEA"/> + <path + android:pathData="M65.8,248.3c10.1,0.7,19.8,3.5,29.7,5.2c19,3.3,38,7,57,10.6c15.9,3,31.8,6.2,47.8,9.2 c24,4.6,48,9.1,72,13.7c14.6,2.8,29.3,5.5,43.8,8.5c22.5,4.6,45.3,8.4,67.9,12.6c4.1,0.8,8.2,1.8,12.4,3.8 c-6.4,2.1,-12.8,4.3,-19.2,6.4c-9.5,3.1,-19.1,5.8,-28.4,9.4c-6.7,2.6,-13.3,2.5,-20,1.3c-41.4,-7.6,-82.8,-15.2,-124.2,-22.8 c-32.6,-6,-65.2,-11.9,-97.8,-17.8c-13.7,-2.5,-27.4,-4.9,-41.1,-7.4C65.9,270,65.9,259.1,65.8,248.3z" + android:fillColor="#E6A3A3"/> + <path + android:pathData="M275,519c-1.5,3.9,-3.5,7.5,-3.3,12.1c0.1,2.9,-2.8,1.7,-4.7,1.4c-12,-2.2,-24.1,-4.3,-36.1,-6.6 c-15.3,-2.9,-30.5,-6,-45.8,-8.7c-5.1,-0.9,-7.5,-2.8,-7.5,-8.4c0,-3.2,-0.8,-6.5,-0.7,-9.9c12.2,2.3,24.4,4.6,36.5,7c19.8,3.9,39.5,8,59.3,12 C273.6,518.2,274.3,518.7,275,519z" + android:fillColor="#CBCBCA"/> + <path + android:pathData="M202.9,345.9c0,3.3,-0.1,6.7,0.1,10c0.2,3.9,-1.4,4.3,-4.9,3.7c-29.9,-5.6,-59.8,-11.2,-89.8,-16.5 c-3,-0.5,-3.5,-1.7,-3.4,-4.2c0.1,-3.3,0.1,-6.7,0.1,-10c21.7,3.9,43.4,7.9,65.2,11.6C181.1,342.4,191.8,345.3,202.9,345.9z" + android:fillColor="#CFCFCE"/> + <path + android:pathData="M65.8,248.3c0,10.9,0,21.7,0,32.6c-17.9,-3.1,-35.8,-6.2,-53.7,-9.3c0,-0.4,0,-0.7,0,-1.1 C30.1,263,48,255.6,65.8,248.3z" + android:fillColor="#E57474"/> + <path + android:pathData="M202.9,345.9c-11.1,-0.6,-21.8,-3.5,-32.6,-5.4c-21.8,-3.7,-43.5,-7.7,-65.2,-11.6c-0.6,-3.8,0.6,-5.6,4.8,-4.8 c28.3,5,56.6,9.9,84.9,14.9c1.1,0.2,2.3,0.8,3.4,0.8C202.8,339.6,203.4,342.3,202.9,345.9z" + android:fillColor="#BDBDBD"/> + <path + android:pathData="M367,876c7.8,-0.4,15,1.9,22,5C381.4,880.3,374.2,878.4,367,876z" + android:fillColor="#EFEFEE"/> + <path + android:pathData="M354,873c4.5,0.1,9.1,0.1,13,3C362.5,875.6,358.1,875,354,873z" + android:fillColor="#EFEFEE"/> + <path + android:pathData="M350,872c1.4,0.1,3,-0.5,4,1C352.6,872.9,351,873.5,350,872z" + android:fillColor="#EFEFEE"/> + <path + android:pathData="M274.1,705c1.9,3.6,1,7.5,0.6,11.2c-0.3,2.8,-2.5,3.1,-4.9,2.6c-29.7,-5.9,-59.3,-11.9,-89,-17.7 c-3.1,-0.6,-3.9,-1.9,-3.8,-4.6c0.1,-3.2,0,-6.3,0,-9.5c1.2,0,2.4,-0.1,3.5,0.1c19.2,3.8,38.4,7.7,57.6,11.4 C250.1,700.8,261.9,703.8,274.1,705z" + android:fillColor="#D6D6D5"/> + <path + android:pathData="M274.1,705c-12.1,-1.2,-24,-4.2,-35.9,-6.5c-19.2,-3.7,-38.4,-7.6,-57.6,-11.4c-1.1,-0.2,-2.3,-0.1,-3.5,-0.1 c-0.5,-4.2,0.7,-5.4,5.3,-4.5c29.5,5.7,59.1,11.1,88.7,16.5C275.3,699.7,275.1,702,274.1,705z" + android:fillColor="#C9C9C8"/> + <path + android:pathData="M409,217.4c0,27.3,0,54.6,0,82c0,6.8,0,6.9,-6.5,5.7c-22.7,-4.2,-45.5,-8.6,-68.2,-12.8 c-27.3,-5.1,-54.6,-10.1,-81.9,-15.3c-22.1,-4.2,-44.1,-8.6,-66.2,-12.8c-27.3,-5.2,-54.6,-10.1,-81.9,-15.2c-10.1,-1.9,-20.1,-4.1,-30.2,-5.8 c-3,-0.5,-3.9,-2.1,-4,-4.6c-0.1,-1,0,-2,0,-3c0,-53,0,-105.9,0,-158.9c0,-6.4,0,-6.6,6.1,-5.6c20,3.4,40,7,60.1,10.5c22.1,3.9,44.3,7.6,66.4,11.5 c17.7,3.1,35.5,6.3,53.2,9.4c24.1,4.2,48.2,8.4,72.3,12.6c23.4,4.1,46.9,8.2,70.3,12.6c10.8,2,10.7,2.3,10.7,13.2 C409,166.4,409,191.9,409,217.4z M283.9,146.9c0.4,-3.2,-0.2,-5.3,-4,-6c-29.7,-5,-59.4,-9.9,-89,-15.3c-4.8,-0.9,-5.2,0.7,-4.8,4.4 c0,3.5,-0.1,7,0,10.5c0,1.3,-0.4,3.2,1.4,3.3c2.9,0.1,5.3,1.8,8.1,2.3c13.8,2.4,27.6,4.9,41.4,7.4c13.3,2.4,26.5,5.1,39.8,7.4 c6.6,1.2,7.3,0.4,7.3,-6.5C284,151.9,283.9,149.4,283.9,146.9z" + android:fillColor="#E8E7E7"/> + <path + android:pathData="M283.9,146.9c0,2.5,0.1,5,0.1,7.5c0,6.9,-0.7,7.7,-7.3,6.5c-13.3,-2.4,-26.5,-5,-39.8,-7.4 c-13.8,-2.5,-27.6,-5.1,-41.4,-7.4c-2.8,-0.5,-5.2,-2.2,-8.1,-2.3c-1.8,-0.1,-1.4,-2,-1.4,-3.3c0,-3.5,0,-7,0,-10.5c1.9,0.3,3.9,0.7,5.8,1 c21.6,4,43.1,8.1,64.7,11.8C265.6,144.4,274.5,147.1,283.9,146.9z" + android:fillColor="#CFCFCE"/> + <path + android:pathData="M283.9,146.9c-9.3,0.2,-18.3,-2.5,-27.3,-4.1c-21.6,-3.7,-43.1,-7.8,-64.7,-11.8c-1.9,-0.4,-3.9,-0.7,-5.8,-1 c-0.4,-3.6,-0.1,-5.2,4.8,-4.4c29.6,5.4,59.3,10.4,89,15.3C283.7,141.6,284.3,143.7,283.9,146.9z" + android:fillColor="#BDBDBD"/> +</vector> diff --git a/packages/DocumentsUI/res/drawable/hourglass.xml b/packages/DocumentsUI/res/drawable/hourglass.xml new file mode 100644 index 000000000000..9b8d0e2946ba --- /dev/null +++ b/packages/DocumentsUI/res/drawable/hourglass.xml @@ -0,0 +1,168 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="421dp" + android:height="909dp" + android:viewportWidth="421.0" + android:viewportHeight="909.0"> + <path + android:pathData="M36,122.9c-2.8,-2.6,-5.7,-5.1,-8.3,-7.8c-5.6,-6,-9.2,-12.9,-8.8,-21.5c0.3,-7.5,0.6,-15,-0.1,-22.5 c-1.2,-14.1,5.5,-23.9,16,-31.9c16.7,-12.8,36.1,-19.6,56.1,-25.1c23.8,-6.5,48,-10.2,72.5,-12.3C168.6,1.3,174,2.2,179,0 c19.3,0,38.7,0,58,0c6,2.1,12.4,1.3,18.6,1.8c30.2,2.7,59.9,7.6,88.5,17.5c16.5,5.7,32.6,12.6,45.2,25.4c6.5,6.6,10.3,14,9.8,23.6 c-0.4,7.8,-0.5,15.7,0,23.4c0.6,10.3,-3.4,18.4,-10.6,25.2c-2.2,2,-4.4,4,-6.6,6c-3,2,-6.1,4,-9.1,5.9c-9.2,4.5,-18.5,9,-28.2,12.3 c-42.4,14.5,-86.3,18.8,-130.8,19.6c-10.9,0.2,-21.9,-0.4,-32.9,-0.7c-4.6,-0.4,-9.2,-0.8,-13.9,-1.1c-18.9,-1.1,-37.5,-3.9,-56,-7.6 c-15.3,-3.1,-30.2,-7.6,-44.9,-12.6c-7.6,-3.7,-15.3,-7.4,-22.9,-11.1C41.1,125.8,38.6,124.3,36,122.9z M41,72c2.9,6.9,7.1,12.6,13.1,17.2 c13,10,27.9,15.8,43.4,20.6c28.2,8.8,57.2,12.8,86.5,14c31.8,1.2,63.7,0.8,95.3,-4.6c25.3,-4.4,50.1,-10,72.9,-22.2 c10.8,-5.8,20,-13.1,24.7,-24.9c2.3,-11,-2.3,-19.5,-10.2,-26.4c-10.5,-9.2,-23.1,-14.9,-36.2,-19.6C295.2,13.4,258.4,9.7,221.2,8.1 c-11.1,-0.5,-22.2,0,-33.4,0.6c-21.4,1,-42.6,3.1,-63.6,7.3c-22.2,4.5,-44.1,10.3,-63.4,22.6C48.9,46.3,38.4,55.4,41,72z" + android:fillColor="#9F9F9F"/> + <path + android:pathData="M0,829c3.7,-2.8,4.7,-7.6,7.8,-10.9c2.6,-2.8,4.9,-5.7,9.2,-7.6c0,3.4,-0.1,6.5,0,9.5c0,1.5,-0.7,3.5,1.7,4 c0.4,3.3,1.4,6.4,2.9,9.4c3.8,7.7,10,13,16.8,17.9c9.2,6.7,19.7,10.8,29.8,15.5c-0.7,2.4,1.3,0.7,1.8,1.1l0,0c1.5,2.1,3.7,2.2,6,2 l0,0c0.8,0.6,1.5,1.4,2.4,1.7c9.5,2.7,18.9,5.8,28.7,7.4c3.6,0.6,7,3.5,10.9,1.1c2.4,0.4,4.8,0.8,7.1,1.2c0.2,1.5,1.3,1.6,2.5,1.8 c6.6,0.9,13.3,2.4,19.9,2.8c5.1,0.3,10.3,2.9,15.4,0.3c0.4,0,0.8,0.1,1.1,0.1c0.3,2.2,2.1,1.8,3.5,1.8c3.8,0,7.6,0,11.5,0 c1.1,1.4,2.7,1,4.1,1c17.2,0,34.5,0,51.7,0c1.4,0,3,0.4,4.1,-1c3.8,0,7.6,0,11.5,0c1.4,0,3.2,0.4,3.5,-1.8c9.4,-1,18.7,-2.1,28.1,-3.1 c6,1.3,11.6,0.4,16.9,-2.8c21.2,-4.2,42.1,-9.3,61.8,-18.4c15.8,-7.3,30.8,-15.8,38,-33.1c2.4,-2,1.9,-4.8,2.2,-7.4c0.3,-3,0,-6,0.1,-9 c0,-1,-0.3,-2.1,0.7,-2.7c1.1,-0.7,1.7,0.5,2.5,1c7.2,5,12.1,11.7,14.8,20c0.4,1.2,0.6,2.2,2.1,2.3c0,3.3,0,6.7,0,10 c-1.5,0,-1.8,1.1,-2.2,2.2c-3.8,10.2,-11.2,17.5,-20.1,23.3c-20.8,13.6,-44.1,21.2,-68.1,26.7c-29.2,6.7,-58.7,11,-88.7,11.7 c-1.6,0,-3.5,-0.5,-3.9,2c-18.7,0,-37.3,0,-56,0c-0.3,-2.5,-2.3,-1.9,-3.9,-2c-5.6,-0.1,-11.2,-0.5,-16.9,-0.8c-18.5,-1.2,-36.8,-3.8,-55,-7.3 C79.9,893.9,54,887,30.2,874C19,867.9,8.5,860.9,2.5,849C2,848,1.4,847,0,847C0,841,0,835,0,829z" + android:fillColor="#E6E4E4"/> + <path + android:pathData="M372.9,128.9c3,-2,6.1,-4,9.1,-5.9c-0.2,2.7,0.2,5.4,1,8c-1.5,1.6,-0.3,1.8,1,2c0.3,1,0.7,2,1,3 c-1.5,1.6,-0.3,1.8,1,2c0.7,2,1.3,4,2,6c-1.5,1.6,-0.3,1.8,1,2c0.3,1.7,0.7,3.3,1,5c-1,2.3,-0.6,4.1,2,5c4.9,23.8,9,47.6,8,72 c-3.5,1.5,-2.1,3.8,-1,6c-1,6,-2,12,-3,18c-1.3,1,-1.3,2,0,3c0,0.7,0,1.3,0,2c-2.1,0.4,-2.6,1.3,-1,3c0,0.3,0,0.7,0,1 c-1.3,0.2,-2.5,0.4,-1,2c0.4,2.1,-0.7,4,-1,6c-1.3,0.2,-2.5,0.4,-1,2c-3.7,9.3,-7.3,18.7,-11,28c-2.7,1.2,-4.2,2.9,-3,6 c-3.4,6.9,-7.8,13.3,-12.2,19.5c-6.1,8.6,-12.4,17.3,-19.4,25.2c-7.3,8.3,-15.5,15.8,-23.9,23c-11.9,10.3,-24.9,19.3,-38.1,27.7 c-12.2,7.8,-25.4,14.1,-38.4,20.5c-12.1,6,-18.5,15.8,-21,28.6c-1.5,7.8,-0.5,15.4,2,22.8c1.2,3.5,3.7,6.1,5.6,9.2 c5.4,8.6,14.8,10.5,22.6,15c15.3,9,30.8,17.7,45.3,28.1c14.4,10.4,28.2,21.5,40.5,34.1c10.1,10.4,18.5,22.2,26.8,34.3 c6.5,9.5,11.3,19.6,16.1,29.8c-1.5,1.6,-0.3,1.8,1,2c0.7,2,1.3,4,2,6c-1,2.3,-0.6,4.1,2,5c0.7,1.2,1.2,2.5,1,4c-1,2.3,-0.6,4.1,2,5 c1.2,12.3,4.6,24.1,5.7,36.5c0.8,8.4,1.4,16.8,1,25.1c-0.3,5.9,-1.1,12,-1.9,18c-1.2,8.7,-2.3,17.4,-4.2,25.9 c-1.5,6.6,-3.7,13.1,-5.6,19.6c-1.8,3.1,-2.9,6.5,-3.9,9.9c-3.4,6.4,-5.6,13.6,-11.9,18.2c-0.1,-3.8,1.6,-7.1,3,-10.4 c8.7,-20.9,13,-42.8,14.7,-65.1c1,-12.9,0.2,-25.8,-1.7,-38.7c-2.7,-18.5,-7.8,-36.2,-15.8,-53.1c-7.3,-15.4,-16.8,-29.3,-27.7,-42.4 c-2.7,-3.2,-6.3,-5.7,-9.6,-8.6c0.4,0.8,0.7,1.4,1,1.9c0.7,1.1,1.5,2.2,2.3,3.3c16.5,21.5,28.5,45.2,34.2,71.7c0.7,3.3,3.1,6.9,0.3,10.4 c-1,-1.9,-2.1,-3.7,-3.1,-5.6c-3.3,-6.3,-6.1,-12.9,-11.7,-17.6c-0.4,-0.9,-0.8,-1.8,-1.3,-2.6c-4,-6.3,-10.4,-10.4,-14.8,-16.2c0,-5.4,-2.7,-9.9,-4.8,-14.5 c-8.4,-18.6,-20.4,-34.9,-32.9,-50.8c-8.4,-10.8,-15.5,-22.8,-28.7,-28.8c-5.3,-2.4,-10,-6,-15.1,-8.6c-5.1,-2.6,-9.9,-6.4,-16.3,-5.2 c-5.2,1,-10.4,2.1,-15.3,4.1c-29.3,11.9,-48.4,34.1,-61.8,61.9c-0.3,0.3,-0.7,0.6,-1,1c-7.1,0.8,-13.9,2.9,-20.7,5.1 c-32.6,10.6,-61,27.4,-82.3,54.9c-9.2,11.6,-15.4,24.7,-18.9,39c-1.5,-1.1,-1.1,-2.7,-1.1,-4.1c-0.1,-9.6,0.3,-19.2,1.8,-28.7 c3.7,-22.6,11.8,-43.5,24,-62.8c12.6,-20,28.6,-36.9,47,-51.7c21.3,-17.3,44.6,-31.3,69.3,-42.9c14.5,-6.8,20,-18.8,21.8,-33.1 c1.8,-13.3,-4.9,-24.1,-12.2,-34.4c-3.6,-5,-7.4,-9.8,-13.2,-12.5c-5.8,-2.8,-11.6,-5.7,-17.4,-8.7c-22,-11.6,-42.6,-25.1,-61.1,-41.7 c-20.7,-18.6,-37.9,-40,-48.8,-65.9c-6.7,-15.7,-10.9,-32.1,-12,-49c-1.8,-27.1,2.1,-53.6,11.7,-79.1c3.8,-10.1,7.1,-20.4,13.3,-29.4 c14.7,5,29.6,9.5,44.9,12.6c18.5,3.7,37.2,6.5,56,7.6c4.6,0.3,9.3,0.7,13.9,1.1c0.2,3.6,-1.5,6.8,-2.6,10 c-11.9,33.6,-17.8,68.2,-17.2,103.8c0.2,9.7,1.3,19.4,2.7,29.1c3.8,24.6,11.4,47.7,26,68.1c12.2,17.1,28.4,28.6,49,33.4 c4.7,1.1,9.5,2.2,14.5,-0.5c18.7,-10.2,36.8,-21.2,53.7,-34.2c15.4,-11.9,29.4,-25.3,41.5,-40.5c12.9,-16.2,23.4,-33.8,30.4,-53.4 c6.4,-17.6,10.3,-35.6,10.9,-54.3c0.5,-15.9,0.2,-31.9,-3,-47.6C383.8,158.7,380.1,143.3,372.9,128.9z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M383,780c1.1,-3.4,2.1,-6.8,3.9,-9.9c7.8,7.1,12.8,15.2,12.2,26.4c-0.6,10.7,-0.3,21.5,-0.5,32.3 c-7.2,17.3,-22.2,25.8,-38,33.1c-19.7,9.1,-40.6,14.1,-61.8,18.4c-5.6,0.9,-11.3,1.9,-16.9,2.8c-9.4,1,-18.7,2.1,-28.1,3.1 c-5,0.3,-9.9,0.7,-14.9,1c-20,1.2,-40,0.9,-60,0c-5,-0.3,-9.9,-0.7,-14.9,-1c-0.4,0,-0.8,-0.1,-1.1,-0.1c-12.6,-1.6,-25.2,-3.2,-37.9,-4.8 c-2.4,-0.4,-4.8,-0.8,-7.1,-1.2c-2.6,-0.6,-5.1,-1.3,-7.7,-1.8c-11.6,-2,-22.6,-6.3,-34.2,-8.4c0,0,0,0,0,0c-1.7,-1.6,-3.7,-2.2,-6,-2c0,0,0,0,0,0 c-0.2,-1,-1.1,-0.9,-1.8,-1.1c-10.2,-4.7,-20.6,-8.8,-29.8,-15.5c-6.8,-4.9,-13,-10.2,-16.8,-17.9c-1.5,-3,-2.4,-6.1,-2.9,-9.4 c0.1,-10.3,0,-20.6,0.2,-30.9c0.1,-8.3,3.5,-15,10,-20.2c2,3.5,3.4,7.1,4.1,11c-1.1,0.8,-1,2,-1.1,3.1c-0.6,8.2,2.9,14.9,8.3,20.6 c8.9,9.6,20.4,15.4,32.3,20.2c17.9,7.2,36.5,11.9,55.4,15.4c20.1,3.7,40.3,5.7,60.6,6.5c21.4,0.8,42.8,0.4,64.2,-1.6 c19.8,-1.9,39.4,-4.6,58.7,-9.3c19.9,-4.8,39.3,-11.2,56.4,-22.9c7.8,-5.3,15.2,-11.3,17.4,-21C386.4,790.1,388.3,784.3,383,780z" + android:fillColor="#9F9F9F"/> + <path + android:pathData="M378,305c-1.2,-3.1,0.3,-4.8,3,-6C380.6,301.3,379.7,303.3,378,305z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M399,234c-1.1,-2.2,-2.5,-4.5,1,-6C399.7,230,400.8,232.2,399,234z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M392,156c-2.6,-0.9,-3,-2.7,-2,-5C391.4,152.4,391.6,154.2,392,156z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M389,636c-2.6,-0.9,-3,-2.7,-2,-5C388.4,632.4,388.6,634.2,389,636z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M392,645c-2.6,-0.9,-3,-2.7,-2,-5C391.4,641.4,391.6,643.2,392,645z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M396,255c-1.3,-1,-1.3,-2,0,-3C397.3,253,397.3,254,396,255z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M395,260c-1.6,-1.7,-1.1,-2.6,1,-3C395.7,258,395.3,259,395,260z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M384,133c-1.3,-0.2,-2.5,-0.4,-1,-2C383.8,131.4,384,132.2,384,133z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M385,625c-1.3,-0.2,-2.5,-0.4,-1,-2C384.8,623.4,385,624.2,385,625z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M392,271c-1.5,-1.6,-0.3,-1.8,1,-2C393,269.8,392.8,270.6,392,271z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M394,263c-1.5,-1.6,-0.3,-1.8,1,-2C395,261.8,394.8,262.6,394,263z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M386,138c-1.3,-0.2,-2.5,-0.4,-1,-2C385.8,136.4,386,137.2,386,138z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M389,146c-1.3,-0.2,-2.5,-0.4,-1,-2C388.8,144.4,389,145.2,389,146z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M33.1,783.9c-0.8,-3.9,-2.2,-7.6,-4.1,-11c-3.7,-11.7,-7.2,-23.5,-9.2,-35.5c-1.3,-7.6,-1.9,-15.4,-2.7,-23.2 c-0.6,-6.2,-0.9,-12.4,-1,-18.5c-0.2,-7.9,1.9,-15.7,2.3,-23.4c0.5,-10.1,2.9,-19.5,5.5,-29c6.3,-23.1,17.3,-43.9,31.5,-62.8 c23.4,-31.1,53.3,-54.7,86.9,-74.1c10.1,-5.8,20.3,-11.6,30.9,-16.2c11.7,-5.2,16.3,-14.9,18.8,-26.3c3.2,-14.9,-2.8,-26.6,-12.7,-37.1 c-1.8,-1.9,-3.9,-3.1,-6.2,-4.2c-23.7,-11.4,-46.4,-24.4,-67.2,-40.7c-16.2,-12.6,-31.3,-26.5,-44.2,-42.6c-16.2,-20.3,-28.8,-42.5,-36.2,-67.5 c-3.1,-10.6,-5.4,-21.3,-6.2,-32.4c-0.7,-9.6,-3.2,-19.3,-2,-28.8c0.8,-6.6,1.5,-13.4,1.9,-20c1,-15.2,4.9,-29.6,9.2,-44c1.7,-5.7,4,-11.3,6.4,-16.8 c0.9,-2.2,1.3,-4.4,1.3,-6.8c2.6,1.4,5.1,2.9,7.2,4.9c-0.6,0.8,-1.4,1.4,-1.8,2.3c-11.2,26.2,-16.2,53.8,-17.4,82.2 c-0.4,8.8,1,17.5,1.9,26.2c2,19.7,7.4,38.4,15.6,56.3c17.9,39.2,46.6,69.1,81.3,93.6c18.5,13.1,38.1,24.3,58.5,34.1 c3.3,1.6,6,3.7,8.1,6.5c8,10.6,12.6,22.1,9.6,35.7c-2.1,9.4,-6.5,17.9,-14.8,22.7c-8.2,4.7,-16.9,8.5,-25.3,12.9 c-22.5,12,-43.8,25.8,-62.6,43c-21.9,19.9,-40.8,42.1,-53.7,69.2C26.3,646.5,20.6,682,24.1,719c1.8,18.7,7,36.7,12.7,54.6 c5.9,18.7,18.2,30.9,35.3,38.9c15.4,7.2,31.5,12.2,48.1,15.8c1.6,0.4,4.3,-0.3,4.8,2.6c-18,-2.8,-35.4,-7.5,-52.3,-14.5 c-12.2,-5.1,-23.4,-11.4,-32.4,-21.4C37.2,791.7,36.5,787,33.1,783.9z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M372.9,128.9c7.2,14.3,10.9,29.8,14.1,45.4c3.2,15.7,3.5,31.6,3,47.6c-0.6,18.7,-4.6,36.7,-10.9,54.3 c-7.1,19.6,-17.6,37.2,-30.4,53.4c-12.1,15.2,-26.1,28.6,-41.5,40.5c-16.9,13,-35,24,-53.7,34.2c-5,2.7,-9.8,1.6,-14.5,0.5 c-20.6,-4.8,-36.8,-16.3,-49,-33.4c-14.6,-20.4,-22.3,-43.5,-26,-68.1c-1.5,-9.7,-2.6,-19.4,-2.7,-29.1c-0.6,-35.6,5.4,-70.2,17.2,-103.8 c1.2,-3.3,2.8,-6.4,2.6,-10c11,0.2,21.9,0.9,32.9,0.7c44.4,-0.8,88.4,-5.2,130.8,-19.6C354.4,137.9,363.7,133.5,372.9,128.9z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M377,72c-4.6,11.9,-13.9,19.1,-24.7,24.9c-22.9,12.2,-47.6,17.9,-72.9,22.2c-31.6,5.4,-63.5,5.9,-95.3,4.6 c-29.3,-1.1,-58.3,-5.1,-86.5,-14C82.1,105,67.2,99.1,54.2,89.2C48.2,84.6,43.9,78.8,41,72c3.9,-1.8,4.6,-6.2,7.3,-9 c10.3,-10.5,22.9,-16.9,36.5,-21.8c19.7,-7.1,40,-11.5,60.7,-14.5c19.4,-2.8,38.9,-3.9,58.4,-4.5c17.9,-0.6,35.8,0.8,53.6,2.8 c15,1.6,29.9,3.5,44.5,7.1c20,4.9,39.7,10.7,57.2,22.1C366.5,58.8,371.5,65.5,377,72z" + android:fillColor="#8D8E8E"/> + <path + android:pathData="M125,831c-0.4,-2.9,-3.2,-2.3,-4.8,-2.6c-16.6,-3.7,-32.7,-8.7,-48.1,-15.8c-17.1,-8,-29.4,-20.2,-35.3,-38.9 c-5.7,-17.9,-10.9,-35.9,-12.7,-54.6c-3.6,-37.1,2.1,-72.5,18.4,-106.4c13,-27.1,31.9,-49.3,53.7,-69.2c18.8,-17.1,40.2,-30.9,62.6,-43 c8.4,-4.5,17.1,-8.2,25.3,-12.9c8.4,-4.8,12.7,-13.3,14.8,-22.7c3.1,-13.6,-1.6,-25.1,-9.6,-35.7c-2.1,-2.8,-4.8,-4.9,-8.1,-6.5 c-20.4,-9.9,-40,-21.1,-58.5,-34.1c-34.7,-24.6,-63.4,-54.5,-81.3,-93.6c-8.2,-17.9,-13.6,-36.6,-15.6,-56.3c-0.9,-8.7,-2.3,-17.5,-1.9,-26.2 c1.2,-28.3,6.2,-55.9,17.4,-82.2c0.4,-0.9,1.2,-1.5,1.8,-2.3c7.6,3.7,15.3,7.4,22.9,11.1c-6.2,9,-9.5,19.3,-13.3,29.4 c-9.6,25.5,-13.5,52,-11.7,79.1c1.1,16.9,5.4,33.3,12,49c11,25.9,28.1,47.4,48.8,65.9c18.5,16.6,39.1,30.2,61.1,41.7 c5.7,3,11.5,5.9,17.4,8.7c5.8,2.8,9.7,7.6,13.2,12.5c7.3,10.3,14,21.1,12.2,34.4c-1.9,14.3,-7.4,26.3,-21.8,33.1 c-24.7,11.6,-48,25.7,-69.3,42.9c-18.3,14.9,-34.3,31.7,-47,51.7c-12.2,19.3,-20.3,40.2,-24,62.8c-1.6,9.6,-2,19.1,-1.8,28.7 c0,1.4,-0.4,3.1,1.1,4.1c-0.6,14.9,0.1,29.8,3,44.5c4.2,21.4,9.1,42.6,26,58.4c-0.2,2.3,0.9,4.1,2.2,5.9c8.8,12.3,22,18.6,35.3,24.1 c26.4,10.9,54.2,16.1,82.4,19.1c1.7,0.2,3,0.4,3,2.4c-4.5,0.7,-9,0.9,-13.4,0.5c-13.2,-1,-26.5,-1.9,-39.6,-4.1c-0.6,-2.4,-1.7,-2.3,-3.1,-0.6 c-1.3,-0.1,-2.6,-0.3,-3.9,-0.4c-0.6,-2.3,-1.6,-2.4,-3.1,-0.7c-0.6,-0.1,-1.3,-0.2,-1.9,-0.3c-0.6,-2.4,-1.7,-2.4,-3.1,-0.6 C126.2,831.2,125.6,831.1,125,831z" + android:fillColor="#E8E8E7"/> + <path + android:pathData="M377,72c-5.4,-6.4,-10.5,-13.2,-17.7,-17.9C341.7,42.7,322.1,36.9,302,32c-14.6,-3.6,-29.5,-5.5,-44.5,-7.1 c-17.8,-1.9,-35.7,-3.3,-53.6,-2.8c-19.5,0.6,-39,1.7,-58.4,4.5c-20.7,3,-41,7.4,-60.7,14.5C71.3,46.1,58.7,52.4,48.4,63 c-2.7,2.8,-3.5,7.2,-7.3,9c-2.6,-16.6,7.9,-25.6,19.8,-33.3c19.3,-12.3,41.2,-18.2,63.4,-22.6c21,-4.2,42.2,-6.3,63.6,-7.3 c11.1,-0.5,22.3,-1,33.4,-0.6c37.2,1.5,74,5.3,109.4,17.9c13.1,4.6,25.6,10.4,36.2,19.6C374.7,52.5,379.3,61,377,72z" + android:fillColor="#808080"/> + <path + android:pathData="M179,887.2c20,0.9,40,1.2,60,0c0,0.3,0,0.5,0,0.8c-1.1,1.4,-2.7,1,-4.1,1c-17.2,0,-34.5,0,-51.7,0 c-1.4,0,-3,0.4,-4.1,-1C179,887.7,179,887.5,179,887.2z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M76,869.9c11.6,2.2,22.6,6.5,34.2,8.4c2.6,0.4,5.2,1.2,7.7,1.8c-3.9,2.3,-7.3,-0.6,-10.9,-1.1 c-9.8,-1.6,-19.2,-4.7,-28.7,-7.4C77.5,871.3,76.8,870.5,76,869.9z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M125.1,881.3c12.6,1.6,25.2,3.2,37.9,4.8c-5.2,2.6,-10.3,0,-15.4,-0.3c-6.7,-0.4,-13.3,-1.9,-19.9,-2.8 C126.3,882.9,125.2,882.8,125.1,881.3z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M282,883.1c5.6,-0.9,11.3,-1.9,16.9,-2.8C293.7,883.4,288.1,884.4,282,883.1z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M179,887.2c0,0.3,0,0.5,0,0.8c-3.8,0,-7.6,0,-11.5,0c-1.4,0,-3.2,0.4,-3.5,-1.8C169,886.5,174,886.9,179,887.2z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M239,888c0,-0.3,0,-0.5,0,-0.8c5,-0.3,9.9,-0.7,14.9,-1c-0.3,2.2,-2.1,1.8,-3.5,1.8C246.6,888,242.8,888,239,888z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M70,867.9c2.3,-0.2,4.3,0.4,6,2C73.8,870.1,71.6,870,70,867.9z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M68.2,866.8c0.7,0.2,1.6,0.2,1.8,1.1C69.5,867.4,67.6,869.2,68.2,866.8z" + android:fillColor="#F4F3F2"/> + <path + android:pathData="M165,584c0.3,-0.3,0.7,-0.6,1,-1c10.5,-1.3,21,-3.3,31.5,-3.8c20.8,-1.1,41.4,0.7,61.7,5.4 c30.5,7,57.8,20.3,81.8,40.5c4.4,5.9,10.8,10,14.8,16.2c0.5,0.8,0.9,1.7,1.3,2.6c-2.4,4.1,-4.7,8.1,-7.1,12.2c-4,3.7,-6.3,8.9,-11,12 c-1.6,-0.1,-2.8,0.5,-4.1,1.5c-12.6,9.3,-26.7,15.6,-41.5,20.1c-31,9.4,-62.6,13.3,-95.1,11.6c-12.3,-0.6,-24.5,-1.5,-36.5,-3.4 c-4.5,-0.7,-5.3,0.5,-4.8,4.2c-0.5,0,-1,0.1,-1.5,0c-17.8,-3.9,-34.7,-10.3,-50.9,-18.7c-1,-0.5,-1.6,-1.1,-1.6,-2.3c2.3,-0.3,4.1,1.1,6.1,2 c14.2,6.2,28.9,10.6,44.1,13.3c2.5,0.4,3,0.2,2.4,-2.3c-2.5,-9.3,-3.7,-18.7,-4.8,-28.3c-1.5,-13.7,-0.3,-27.1,1.7,-40.5 C154.6,610.8,159.7,597.4,165,584z" + android:fillColor="#E57474"/> + <path + android:pathData="M103,681c0.1,1.1,0.7,1.8,1.6,2.3c16.2,8.4,33.1,14.8,50.9,18.7c0.5,0.1,1,0,1.5,0c0.6,0.4,1.3,0.7,1.9,1.1 c-0.1,2.7,1,5.2,1.9,7.6c6.5,17.8,16.5,33.6,27.5,48.8c12.5,17.1,27.1,32.1,45.1,43.6c6.6,4.2,13.7,7.3,20.5,11 c-15.6,2.8,-31.4,2.5,-47.1,2.4c-9.9,0,-19.9,-0.2,-29.8,-1.1c-18.2,-1.6,-36.2,-3.9,-54,-8.3c-18.1,-4.4,-35.9,-9.5,-51,-21.1 c-16.9,-15.8,-21.8,-37,-26,-58.4c-2.9,-14.7,-3.6,-29.6,-3,-44.5c3.5,-14.4,9.7,-27.4,18.9,-39c3.4,4.8,6.2,10.1,10.3,14.2 c6,6.1,11.6,13,19.7,16.8c0.5,0.8,1.2,1,2.1,1c0,0,0,0,0,0c0.3,0.3,0.7,0.7,1,1c0,0,0,0,0,0c0.3,0.3,0.7,0.7,1,1l0,0 c1,1.3,2.4,1.8,4,2l0,0C100.7,681.2,101.9,681,103,681L103,681z" + android:fillColor="#E6A3A3"/> + <path + android:pathData="M341,625c-24,-20.1,-51.3,-33.5,-81.8,-40.5c-20.3,-4.7,-41,-6.5,-61.7,-5.4c-10.5,0.6,-21,2.5,-31.5,3.8 c13.4,-27.8,32.5,-49.9,61.8,-61.9c4.9,-2,10.1,-3.1,15.3,-4.1c6.3,-1.2,11.2,2.6,16.3,5.2c5.2,2.6,9.9,6.2,15.1,8.6 c13.3,6,20.3,18,28.7,28.8c12.5,16,24.6,32.2,32.9,50.8C338.3,615.2,341,619.7,341,625z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M91.9,675c-8,-3.8,-13.6,-10.7,-19.7,-16.8c-4.1,-4.1,-6.9,-9.4,-10.3,-14.2c21.3,-27.5,49.8,-44.3,82.3,-54.9 c6.8,-2.2,13.6,-4.3,20.7,-5.1c-5.3,13.4,-10.4,26.9,-12.5,41.2c-2,13.4,-3.2,26.8,-1.7,40.5c1.1,9.6,2.3,19,4.8,28.3 c0.7,2.5,0.1,2.7,-2.4,2.3c-15.2,-2.6,-29.9,-7.1,-44.1,-13.3c-2,-0.9,-3.7,-2.3,-6.1,-2c0,0,0,0,0,0c-0.7,-1.2,-1.9,-1,-3,-1c0,0,0,0,0,0 c-0.3,-2.7,-2.4,-1.8,-4,-2c0,0,0,0,0,0c-0.3,-0.3,-0.7,-0.7,-1,-1c0,0,0,0,0,0c-0.3,-0.3,-0.7,-0.7,-1,-1c0,0,0,0,0,0 C93.6,675.2,92.8,675,91.9,675z" + android:fillColor="#D86868"/> + <path + android:pathData="M135,832.8c1.3,0.1,2.6,0.3,3.9,0.4c0.9,0.8,2,0.7,3.1,0.6c13.1,2.2,26.4,3,39.6,4.1 c4.5,0.3,9,0.2,13.4,-0.5c3.1,0.3,6.3,0.7,9.4,0.8c22.6,0.4,45.2,-0.9,67.6,-4.1c23.9,-3.3,47.4,-8.2,69.8,-17.6 c10.7,-4.5,21.4,-9.3,29.3,-18.3l0,0.1c6.2,-4.6,8.5,-11.8,11.9,-18.2c5.2,4.3,3.3,10.1,2.2,15c-2.2,9.7,-9.6,15.7,-17.4,21 c-17.1,11.7,-36.5,18.1,-56.4,22.9c-19.3,4.7,-38.9,7.4,-58.7,9.3c-21.4,2,-42.8,2.5,-64.2,1.6c-20.3,-0.8,-40.5,-2.8,-60.6,-6.5 c-19,-3.5,-37.6,-8.2,-55.4,-15.4c-11.9,-4.8,-23.4,-10.6,-32.3,-20.2c-5.3,-5.8,-8.9,-12.4,-8.3,-20.6c0.1,-1.2,0,-2.4,1.1,-3.1 c3.3,3.1,4.1,7.8,7.2,11.1c9,9.9,20.2,16.3,32.4,21.4c16.8,7,34.3,11.7,52.3,14.5c0.6,0.1,1.2,0.2,1.9,0.3c0.9,0.8,2,0.7,3.1,0.6 c0.6,0.1,1.3,0.2,1.9,0.3C132.8,833,133.9,833,135,832.8z" + android:fillColor="#999899"/> + <path + android:pathData="M371.9,667c2.8,-3.5,0.4,-7.1,-0.3,-10.4c-5.7,-26.6,-17.7,-50.3,-34.2,-71.7c-0.8,-1,-1.5,-2.2,-2.3,-3.3 c-0.3,-0.5,-0.6,-1.1,-1,-1.9c3.4,3,6.9,5.4,9.6,8.6c10.8,13.1,20.4,27,27.7,42.4c8,16.9,13.1,34.6,15.8,53.1 c1.9,12.9,2.6,25.8,1.7,38.7c-1.7,22.4,-6,44.3,-14.7,65.1c-1.4,3.3,-3.1,6.6,-3,10.4c0,0,0,-0.1,0,-0.1c-1.8,-0.3,-3.3,0.4,-4.7,1.2 c-6.3,3.7,-12.6,7.3,-19.4,10.2c-24,10.3,-48.8,14.5,-74.7,9.2c-4.2,-0.9,-9.4,-0.2,-12.4,-4.8c13.8,-2,27.6,-4.4,41.1,-8 c10,-2.7,20,-5.4,29,-10.8c1.5,-0.5,3.2,-0.9,4.6,-1.6c10.6,-5.1,19.5,-12.1,25.3,-22.6c4.7,-3.1,8.2,-10.7,6.9,-15c0.3,-1.4,0.6,-2.9,1,-4.3 c5.4,-16.2,7.5,-33.1,9,-50C378,689.7,375.5,678.3,371.9,667z" + android:fillColor="#F1F0F0"/> + <path + android:pathData="M371.9,667c3.6,11.3,6.1,22.7,5.1,34.6c-1.5,16.9,-3.6,33.8,-9,50c-0.5,1.4,-0.7,2.9,-1,4.3 c-2.3,5,-4.6,10,-6.9,15c-5.8,10.5,-14.7,17.5,-25.3,22.6c-1.5,0.7,-3.1,1.1,-4.6,1.6c-0.3,-2.1,0.3,-3.9,1.1,-5.7 c3.4,-7.4,6.4,-14.9,8.9,-22.6c6,-18.1,10,-36.5,12,-55.6c1.2,-11.6,0.9,-23.2,0.6,-34.8c-0.2,-6.8,-0.7,-13.8,-2.8,-20.5 c2.4,-4.1,4.7,-8.1,7.1,-12.2c5.6,4.7,8.4,11.3,11.7,17.6C369.8,663.3,370.8,665.2,371.9,667z" + android:fillColor="#E6A3A3"/> + <path + android:pathData="M260,814c2.9,4.6,8.1,3.9,12.4,4.8c25.9,5.3,50.7,1.1,74.7,-9.2c6.7,-2.9,13.1,-6.4,19.4,-10.2 c1.4,-0.9,2.9,-1.6,4.7,-1.2c-7.9,9.1,-18.6,13.8,-29.3,18.3c-22.3,9.4,-45.9,14.3,-69.8,17.6c-22.4,3.1,-45,4.4,-67.6,4.1 c-3.1,-0.1,-6.3,-0.5,-9.4,-0.8c-0.1,-2,-1.4,-2.2,-3,-2.4c-28.3,-3,-56,-8.2,-82.4,-19.1c-13.4,-5.5,-26.5,-11.8,-35.3,-24.1c-1.3,-1.8,-2.4,-3.6,-2.2,-5.9 c15.1,11.6,32.9,16.7,51,21.1c17.7,4.4,35.8,6.6,54,8.3c10,0.9,20,1.1,29.8,1.1c15.7,0.1,31.5,0.4,47.1,-2.4 C256,814,258,814,260,814z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M130,831.9c-1.1,0.1,-2.2,0.2,-3.1,-0.6C128.3,829.5,129.4,829.5,130,831.9z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M135,832.8c-1.1,0.1,-2.2,0.2,-3.1,-0.7C133.4,830.5,134.4,830.6,135,832.8z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M142,833.8c-1.1,0.1,-2.2,0.2,-3.1,-0.6C140.3,831.5,141.4,831.5,142,833.8z" + android:fillColor="#EDECEC"/> + <path + android:pathData="M260,814c-2,0,-4,0,-6,0c-6.8,-3.7,-13.9,-6.8,-20.5,-11c-18,-11.5,-32.7,-26.4,-45.1,-43.6c-11,-15.2,-21,-31,-27.5,-48.8 c-0.9,-2.5,-2,-4.9,-1.9,-7.7c0.7,0,1.4,-0.1,2,0.1c16.2,4.6,33.1,5.3,49.6,5.3c10,0,20.1,-0.2,30.2,-1.3c19.5,-2,38.7,-5.3,57,-12.4 c15.6,-6,30.1,-13.9,41.3,-26.9c4.7,-3.1,7,-8.3,11,-12c2.1,6.7,2.6,13.7,2.8,20.5c0.3,11.6,0.6,23.1,-0.6,34.8c-2,19,-6,37.5,-12,55.6 c-2.6,7.7,-5.5,15.3,-8.9,22.6c-0.9,1.9,-1.5,3.7,-1.1,5.7c-9,5.4,-19,8.1,-29,10.8C287.6,809.6,273.8,811.9,260,814z" + android:fillColor="#E6A3A3"/> + <path + android:pathData="M339,668c-11.1,13,-25.7,20.8,-41.3,26.9c-18.3,7.1,-37.5,10.4,-57,12.4c-10.1,1,-20.3,1.3,-30.2,1.3 c-16.6,0,-33.4,-0.7,-49.6,-5.3c-0.6,-0.2,-1.3,-0.1,-2,-0.1c-0.6,-0.4,-1.3,-0.7,-1.9,-1.1c-0.4,-3.8,0.3,-5,4.8,-4.2c12.1,2,24.3,2.8,36.5,3.4 c32.4,1.7,64.1,-2.3,95.1,-11.6c14.8,-4.5,29,-10.8,41.5,-20.1C336.3,668.5,337.5,667.9,339,668z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M96,678c1.6,0.2,3.7,-0.7,4,2C98.4,679.8,97,679.3,96,678z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M100,680c1.1,0,2.3,-0.2,3,1C101.9,681,100.7,681.2,100,680z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M91.9,675c0.8,0,1.6,0.2,2.1,1C93.2,676,92.4,675.8,91.9,675z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M94,676c0.3,0.3,0.7,0.7,1,1C94.7,676.7,94.3,676.3,94,676z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M95,677c0.3,0.3,0.7,0.7,1,1C95.7,677.7,95.3,677.3,95,677z" + android:fillColor="#C5C5C5"/> + <path + android:pathData="M360,771c2.3,-5,4.6,-10,6.9,-15C368.2,760.3,364.7,767.8,360,771z" + android:fillColor="#EDECEC"/> +</vector> diff --git a/packages/DocumentsUI/res/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml index 8245e53097cd..2fa09d39bdab 100644 --- a/packages/DocumentsUI/res/layout/directory_cluster.xml +++ b/packages/DocumentsUI/res/layout/directory_cluster.xml @@ -25,7 +25,7 @@ android:elevation="8dp" android:background="@color/material_grey_50"/> - <com.android.documentsui.DirectoryContainerView + <FrameLayout android:id="@+id/container_directory" android:layout_width="match_parent" android:layout_height="0dp" diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml index b791ef12b6f3..73571af20134 100644 --- a/packages/DocumentsUI/res/menu/activity.xml +++ b/packages/DocumentsUI/res/menu/activity.xml @@ -32,23 +32,6 @@ android:imeOptions="actionSearch" android:visible="false" /> <item - android:id="@+id/menu_sort" - android:title="@string/menu_sort" - android:icon="@drawable/ic_menu_sortby" - android:showAsAction="always"> - <menu> - <item - android:id="@+id/menu_sort_name" - android:title="@string/sort_name" /> - <item - android:id="@+id/menu_sort_date" - android:title="@string/sort_date" /> - <item - android:id="@+id/menu_sort_size" - android:title="@string/sort_size" /> - </menu> - </item> - <item android:id="@+id/menu_grid" android:title="@string/menu_grid" android:icon="@drawable/ic_menu_view_grid" @@ -70,7 +53,7 @@ android:title="@string/menu_create_dir" android:icon="@drawable/ic_menu_new_folder" android:alphabeticShortcut="e" - android:showAsAction="always" + android:showAsAction="never" android:visible="false" /> <item android:id="@+id/menu_paste_from_clipboard" @@ -80,6 +63,23 @@ android:visible="false" /> <!-- Copy action is defined in mode_directory.xml --> <item + android:id="@+id/menu_sort" + android:title="@string/menu_sort" + android:icon="@drawable/ic_menu_sortby" + android:showAsAction="never"> + <menu> + <item + android:id="@+id/menu_sort_name" + android:title="@string/sort_name" /> + <item + android:id="@+id/menu_sort_date" + android:title="@string/sort_date" /> + <item + android:id="@+id/menu_sort_size" + android:title="@string/sort_size" /> + </menu> + </item> + <item android:id="@+id/menu_file_size" android:showAsAction="never" android:visible="false" /> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index 3c49f167534e..b97918e7b9d1 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -101,7 +101,7 @@ <!-- Toast shown when creating a folder failed with an error [CHAR LIMIT=48] --> <string name="create_error">Failed to create folder</string> <!-- Error message shown when querying for a list of documents failed [CHAR LIMIT=48] --> - <string name="query_error">Failed to query documents</string> + <string name="query_error">Can\u2019t load content at the moment</string> <!-- Title of storage root location that contains recently modified or used documents [CHAR LIMIT=24] --> <string name="root_recent">Recent</string> @@ -123,7 +123,7 @@ <string name="no_results">No matches in %1$s</string> <!-- Toast shown when no app can be found to open the selected document [CHAR LIMIT=48] --> - <string name="toast_no_application">Can\'t open file</string> + <string name="toast_no_application">Can\u2019t open file</string> <!-- Toast shown when some of the selected documents failed to be deleted [CHAR LIMIT=48] --> <string name="toast_failed_delete">Unable to delete some documents</string> @@ -160,27 +160,27 @@ <string name="delete_preparing">Preparing for delete\u2026</string> <!-- Title of the copy error notification [CHAR LIMIT=48] --> <plurals name="copy_error_notification_title"> - <item quantity="one">Couldn\'t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item> - <item quantity="other">Couldn\'t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item> + <item quantity="one">Couldn\u2019t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item> + <item quantity="other">Couldn\u2019t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item> </plurals> <!-- Title of the move error notification [CHAR LIMIT=48] --> <plurals name="move_error_notification_title"> - <item quantity="one">Couldn\'t move <xliff:g id="count" example="1">%1$d</xliff:g> file</item> - <item quantity="other">Couldn\'t move <xliff:g id="count" example="2">%1$d</xliff:g> files</item> + <item quantity="one">Couldn\u2019t move <xliff:g id="count" example="1">%1$d</xliff:g> file</item> + <item quantity="other">Couldn\u2019t move <xliff:g id="count" example="2">%1$d</xliff:g> files</item> </plurals> <!-- Title of the delete error notification [CHAR LIMIT=48] --> <plurals name="delete_error_notification_title"> - <item quantity="one">Couldn\'t delete <xliff:g id="count" example="1">%1$d</xliff:g> file</item> - <item quantity="other">Couldn\'t delete <xliff:g id="count" example="2">%1$d</xliff:g> files</item> + <item quantity="one">Couldn\u2019t delete <xliff:g id="count" example="1">%1$d</xliff:g> file</item> + <item quantity="other">Couldn\u2019t delete <xliff:g id="count" example="2">%1$d</xliff:g> files</item> </plurals> <!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] --> <string name="notification_touch_for_details">Tap to view details</string> <!-- Label of the close dialog button.[CHAR LIMIT=24] --> <string name="close">Close</string> <!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] --> - <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string> + <string name="copy_failure_alert_content">These files weren\u2019t copied: <xliff:g id="list">%1$s</xliff:g></string> <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] --> - <string name="move_failure_alert_content">These files weren\'t moved: <xliff:g id="list">%1$s</xliff:g></string> + <string name="move_failure_alert_content">These files weren\u2019t moved: <xliff:g id="list">%1$s</xliff:g></string> <!-- Contents of the copying warning dialog due to converted files. [CHAR LIMIT=64] --> <string name="copy_converted_warning_content">These files were converted to another format: <xliff:g id="list" example="Document.pdf, Photo.jpg, Song.ogg">%1$s</xliff:g></string> <!-- Toast shown when a user copies files to clipboard. --> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index e72343e9a9ba..3c21a214b19b 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -45,22 +45,16 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.Spinner; -import android.widget.Toolbar; -import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.SearchManager.SearchManagerListener; import com.android.documentsui.State.ViewMode; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; -import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; -import libcore.io.IoUtils; - import java.io.FileNotFoundException; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -71,6 +65,9 @@ public abstract class BaseActivity extends Activity static final String EXTRA_STATE = "state"; + // See comments where this const is referenced for details. + private static final int DRAWER_NO_FIDDLE_DELAY = 1500; + State mState; RootsCache mRoots; SearchManager mSearchManager; @@ -78,17 +75,21 @@ public abstract class BaseActivity extends Activity NavigationView mNavigator; private final String mTag; + @LayoutRes private int mLayoutId; - private DirectoryContainerView mDirectoryContainer; + + // Track the time we opened the drawer in response to back being pressed. + // We use the time gap to figure out whether to close app or reopen the drawer. + private long mDrawerLastFiddled; public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings); public abstract void onDocumentsPicked(List<DocumentInfo> docs); abstract void onTaskFinished(Uri... uris); abstract void refreshDirectory(int anim); - abstract void saveStackBlocking(); - abstract State buildState(); + /** Allows sub-classes to include information in a newly created State instance. */ + abstract void includeState(State initialState); public BaseActivity(@LayoutRes int layoutId, String tag) { mLayoutId = layoutId; @@ -103,9 +104,7 @@ public abstract class BaseActivity extends Activity setContentView(mLayoutId); mDrawer = DrawerController.create(this); - mState = (icicle != null) - ? icicle.<State>getParcelable(EXTRA_STATE) - : buildState(); + mState = getState(icicle); Metrics.logActivityLaunch(this, mState, getIntent()); mRoots = DocumentsApplication.getRootsCache(this); @@ -114,11 +113,11 @@ public abstract class BaseActivity extends Activity new RootsCache.OnCacheUpdateListener() { @Override public void onCacheUpdate() { - new HandleRootsChangedTask().execute(getCurrentRoot()); + new HandleRootsChangedTask(BaseActivity.this) + .execute(getCurrentRoot()); } }); - mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory); mSearchManager = new SearchManager(this); DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar); @@ -184,7 +183,20 @@ public abstract class BaseActivity extends Activity super.onDestroy(); } - State buildDefaultState() { + private State getState(@Nullable Bundle icicle) { + if (icicle != null) { + State state = icicle.<State>getParcelable(EXTRA_STATE); + if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state); + return state; + } + + State state = createSharedState(); + includeState(state); + if (DEBUG) Log.d(mTag, "Created new state object: " + state); + return state; + } + + private State createSharedState() { State state = new State(); final Intent intent = getIntent(); @@ -224,22 +236,7 @@ public abstract class BaseActivity extends Activity if (mRoots.isRecentsRoot(root)) { refreshCurrentRootAndDirectory(ANIM_NONE); } else { - new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory()); - } - } - - void expandMenus(Menu menu) { - for (int i = 0; i < menu.size(); i++) { - final MenuItem item = menu.getItem(i); - switch (item.getItemId()) { - case R.id.menu_advanced: - case R.id.menu_file_size: - case R.id.menu_new_window: - case R.id.menu_search: - break; - default: - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - } + new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory()); } } @@ -351,7 +348,6 @@ public abstract class BaseActivity extends Activity public final void refreshCurrentRootAndDirectory(int anim) { mSearchManager.cancelSearch(); - mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_ENTER); refreshDirectory(anim); final RootsFragment roots = RootsFragment.get(getFragmentManager()); @@ -370,7 +366,6 @@ public abstract class BaseActivity extends Activity */ @Override public void onSearchChanged() { - mDirectoryContainer.setDrawDisappearingFirst(false); refreshDirectory(ANIM_NONE); } @@ -539,16 +534,35 @@ public abstract class BaseActivity extends Activity return; } - final int size = mState.stack.size(); + int size = mState.stack.size(); + + // Do some "do what a I want" drawer fiddling, but don't + // do it if user already hit back recently and we recently + // did some fiddling. + if ((System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) { + // Close drawer if it is open. + if (mDrawer.isOpen()) { + mDrawer.setOpen(false); + mDrawerLastFiddled = System.currentTimeMillis(); + return; + } + + // Open the Close drawer if it is closed and we're at the top of a root. + if (size == 1) { + mDrawer.setOpen(true); + // Remember so we don't just close it again if back is pressed again. + mDrawerLastFiddled = System.currentTimeMillis(); + return; + } + } - if (mDrawer.isOpen()) { - mDrawer.setOpen(false); - } else if (size > 1) { + if (size > 1) { mState.stack.pop(); refreshCurrentRootAndDirectory(ANIM_LEAVE); - } else { - super.onBackPressed(); + return; } + + super.onBackPressed(); } public void onStackPicked(DocumentStack stack) { @@ -574,115 +588,40 @@ public abstract class BaseActivity extends Activity } } - final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> { + private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> { private RootInfo mRoot; - public PickRootTask(RootInfo root) { + public PickRootTask(BaseActivity activity, RootInfo root) { + super(activity); mRoot = root; } @Override - protected DocumentInfo doInBackground(Void... params) { - return getRootDocumentBlocking(mRoot); + protected DocumentInfo run(Void... params) { + return mOwner.getRootDocumentBlocking(mRoot); } @Override - protected void onPostExecute(DocumentInfo result) { - if (result != null && !isDestroyed()) { - openContainerDocument(result); + protected void finish(DocumentInfo result) { + if (result != null) { + mOwner.openContainerDocument(result); } } } - final class RestoreStackTask extends AsyncTask<Void, Void, Void> { - private volatile boolean mRestoredStack; - private volatile boolean mExternal; - - @Override - protected Void doInBackground(Void... params) { - if (DEBUG && !mState.stack.isEmpty()) { - Log.w(mTag, "Overwriting existing stack."); - } - RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this); - - // Restore last stack for calling package - final String packageName = getCallingPackageMaybeExtra(); - final Cursor cursor = getContentResolver() - .query(RecentsProvider.buildResume(packageName), null, null, null, null); - try { - if (cursor.moveToFirst()) { - mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; - final byte[] rawStack = cursor.getBlob( - cursor.getColumnIndex(ResumeColumns.STACK)); - DurableUtils.readFromArray(rawStack, mState.stack); - mRestoredStack = true; - } - } catch (IOException e) { - Log.w(mTag, "Failed to resume: " + e); - } finally { - IoUtils.closeQuietly(cursor); - } - - if (mRestoredStack) { - // Update the restored stack to ensure we have freshest data - final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); - try { - mState.stack.updateRoot(matchingRoots); - mState.stack.updateDocuments(getContentResolver()); - } catch (FileNotFoundException e) { - Log.w(mTag, "Failed to restore stack: " + e); - mState.stack.reset(); - mRestoredStack = false; - } - } - - return null; - } - - @Override - protected void onPostExecute(Void result) { - if (isDestroyed()) return; - mState.restored = true; - refreshCurrentRootAndDirectory(ANIM_NONE); - onStackRestored(mRestoredStack, mExternal); - } - } - - final class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> { - private Uri mRootUri; - - public RestoreRootTask(Uri rootUri) { - mRootUri = rootUri; - } - - @Override - protected RootInfo doInBackground(Void... params) { - final String rootId = DocumentsContract.getRootId(mRootUri); - return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId); - } + private static final class HandleRootsChangedTask + extends PairedTask<BaseActivity, RootInfo, RootInfo> { + DocumentInfo mHome; - @Override - protected void onPostExecute(RootInfo root) { - if (isDestroyed()) return; - mState.restored = true; - - if (root != null) { - onRootPicked(root); - } else { - Log.w(mTag, "Failed to find root: " + mRootUri); - finish(); - } + public HandleRootsChangedTask(BaseActivity activity) { + super(activity); } - } - - final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> { - DocumentInfo mHome; @Override - protected RootInfo doInBackground(RootInfo... roots) { + protected RootInfo run(RootInfo... roots) { checkArgument(roots.length == 1); final RootInfo currentRoot = roots[0]; - final Collection<RootInfo> cachedRoots = mRoots.getRootsBlocking(); + final Collection<RootInfo> cachedRoots = mOwner.mRoots.getRootsBlocking(); RootInfo homeRoot = null; for (final RootInfo root : cachedRoots) { if (root.isHome()) { @@ -694,17 +633,17 @@ public abstract class BaseActivity extends Activity } } Preconditions.checkNotNull(homeRoot); - mHome = getRootDocumentBlocking(homeRoot); + mHome = mOwner.getRootDocumentBlocking(homeRoot); return homeRoot; } @Override - protected void onPostExecute(RootInfo homeRoot) { - if (homeRoot != null && mHome != null && !isDestroyed()) { + protected void finish(RootInfo homeRoot) { + if (homeRoot != null && mHome != null) { // Clear entire backstack and start in new root - mState.onRootChanged(homeRoot); - mSearchManager.update(homeRoot); - openContainerDocument(mHome); + mOwner.mState.onRootChanged(homeRoot); + mOwner.mSearchManager.update(homeRoot); + mOwner.openContainerDocument(mHome); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java deleted file mode 100644 index 71ea8a9352f6..000000000000 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 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.documentsui; - -import android.content.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import java.util.ArrayList; - -public class DirectoryContainerView extends FrameLayout { - private boolean mDisappearingFirst = false; - - public DirectoryContainerView(Context context) { - super(context); - } - - public DirectoryContainerView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - final ArrayList<View> disappearing = mDisappearingChildren; - if (mDisappearingFirst && disappearing != null) { - for (int i = 0; i < disappearing.size(); i++) { - super.drawChild(canvas, disappearing.get(i), getDrawingTime()); - } - } - super.dispatchDraw(canvas); - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (mDisappearingFirst && mDisappearingChildren != null - && mDisappearingChildren.contains(child)) { - return false; - } - return super.drawChild(canvas, child, drawingTime); - } - - public void setDrawDisappearingFirst(boolean disappearingFirst) { - mDisappearingFirst = disappearingFirst; - } -} diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 815ff3db9517..3485fe4446f5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -16,6 +16,7 @@ package com.android.documentsui; +import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.State.ACTION_CREATE; import static com.android.documentsui.State.ACTION_GET_CONTENT; import static com.android.documentsui.State.ACTION_OPEN; @@ -31,11 +32,12 @@ import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; @@ -52,7 +54,12 @@ import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; +import libcore.io.IoUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.List; public class DocumentsActivity extends BaseActivity { @@ -94,16 +101,14 @@ public class DocumentsActivity extends BaseActivity { // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent // talkback from reading aloud the default title, we clear it here. setTitle(""); - new RestoreStackTask().execute(); + new RestoreStackTask(this).execute(); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override - State buildState() { - State state = buildDefaultState(); - + void includeState(State state) { final Intent intent = getIntent(); final String action = intent.getAction(); if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) { @@ -134,8 +139,6 @@ public class DocumentsActivity extends BaseActivity { state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_COPY); } - - return state; } @Override @@ -218,14 +221,6 @@ public class DocumentsActivity extends BaseActivity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { - boolean showMenu = super.onCreateOptionsMenu(menu); - - expandMenus(menu); - return showMenu; - } - - @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); @@ -319,11 +314,13 @@ public class DocumentsActivity extends BaseActivity { } void onSaveRequested(DocumentInfo replaceTarget) { - new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); + new ExistingFinishTask(this, replaceTarget.derivedUri) + .executeOnExecutor(getExecutorForCurrentDirectory()); } void onSaveRequested(String mimeType, String displayName) { - new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory()); + new CreateFinishTask(this, mimeType, displayName) + .executeOnExecutor(getExecutorForCurrentDirectory()); } @Override @@ -343,7 +340,8 @@ public class DocumentsActivity extends BaseActivity { openContainerDocument(doc); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) { // Explicit file picked, return - new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); + new ExistingFinishTask(this, doc.derivedUri) + .executeOnExecutor(getExecutorForCurrentDirectory()); } else if (mState.action == ACTION_CREATE) { // Replace selected file SaveFragment.get(fm).setReplaceTarget(doc); @@ -358,7 +356,8 @@ public class DocumentsActivity extends BaseActivity { for (int i = 0; i < size; i++) { uris[i] = docs.get(i).derivedUri; } - new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory()); + new ExistingFinishTask(this, uris) + .executeOnExecutor(getExecutorForCurrentDirectory()); } } @@ -373,11 +372,10 @@ public class DocumentsActivity extends BaseActivity { // Should not be reached. throw new IllegalStateException("Invalid mState.action."); } - new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory()); + new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory()); } - @Override - void saveStackBlocking() { + void writeStackToRecentsBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); @@ -438,69 +436,137 @@ public class DocumentsActivity extends BaseActivity { finish(); } + public static DocumentsActivity get(Fragment fragment) { return (DocumentsActivity) fragment.getActivity(); } - private final class PickFinishTask extends AsyncTask<Void, Void, Void> { + /** + * Restores the stack from Recents for the specified package. + */ + private static final class RestoreStackTask + extends PairedTask<DocumentsActivity, Void, Void> { + + private volatile boolean mRestoredStack; + private volatile boolean mExternal; + private State mState; + + public RestoreStackTask(DocumentsActivity activity) { + super(activity); + mState = activity.mState; + } + + @Override + protected Void run(Void... params) { + if (DEBUG && !mState.stack.isEmpty()) { + Log.w(TAG, "Overwriting existing stack."); + } + RootsCache roots = DocumentsApplication.getRootsCache(mOwner); + + String packageName = mOwner.getCallingPackageMaybeExtra(); + Uri resumeUri = RecentsProvider.buildResume(packageName); + Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null); + try { + if (cursor.moveToFirst()) { + mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; + final byte[] rawStack = cursor.getBlob( + cursor.getColumnIndex(ResumeColumns.STACK)); + DurableUtils.readFromArray(rawStack, mState.stack); + mRestoredStack = true; + } + } catch (IOException e) { + Log.w(TAG, "Failed to resume: " + e); + } finally { + IoUtils.closeQuietly(cursor); + } + + if (mRestoredStack) { + // Update the restored stack to ensure we have freshest data + final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); + try { + mState.stack.updateRoot(matchingRoots); + mState.stack.updateDocuments(mOwner.getContentResolver()); + } catch (FileNotFoundException e) { + Log.w(TAG, "Failed to restore stack for package: " + packageName + + " because of error: "+ e); + mState.stack.reset(); + mRestoredStack = false; + } + } + + return null; + } + + @Override + protected void finish(Void result) { + mState.restored = true; + mOwner.refreshCurrentRootAndDirectory(ANIM_NONE); + mOwner.onStackRestored(mRestoredStack, mExternal); + } + } + + private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri mUri; - public PickFinishTask(Uri uri) { + public PickFinishTask(DocumentsActivity activity, Uri uri) { + super(activity); mUri = uri; } @Override - protected Void doInBackground(Void... params) { - saveStackBlocking(); + protected Void run(Void... params) { + mOwner.writeStackToRecentsBlocking(); return null; } @Override - protected void onPostExecute(Void result) { - onTaskFinished(mUri); + protected void finish(Void result) { + mOwner.onTaskFinished(mUri); } } - final class ExistingFinishTask extends AsyncTask<Void, Void, Void> { + private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri[] mUris; - public ExistingFinishTask(Uri... uris) { + public ExistingFinishTask(DocumentsActivity activity, Uri... uris) { + super(activity); mUris = uris; } @Override - protected Void doInBackground(Void... params) { - saveStackBlocking(); + protected Void run(Void... params) { + mOwner.writeStackToRecentsBlocking(); return null; } @Override - protected void onPostExecute(Void result) { - onTaskFinished(mUris); + protected void finish(Void result) { + mOwner.onTaskFinished(mUris); } } /** * Task that creates a new document in the background. */ - final class CreateFinishTask extends AsyncTask<Void, Void, Uri> { + private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> { private final String mMimeType; private final String mDisplayName; - public CreateFinishTask(String mimeType, String displayName) { + public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) { + super(activity); mMimeType = mimeType; mDisplayName = displayName; } @Override - protected void onPreExecute() { - setPending(true); + protected void prepare() { + mOwner.setPending(true); } @Override - protected Uri doInBackground(Void... params) { - final ContentResolver resolver = getContentResolver(); - final DocumentInfo cwd = getCurrentDirectory(); + protected Uri run(Void... params) { + final ContentResolver resolver = mOwner.getContentResolver(); + final DocumentInfo cwd = mOwner.getCurrentDirectory(); ContentProviderClient client = null; Uri childUri = null; @@ -516,22 +582,22 @@ public class DocumentsActivity extends BaseActivity { } if (childUri != null) { - saveStackBlocking(); + mOwner.writeStackToRecentsBlocking(); } return childUri; } @Override - protected void onPostExecute(Uri result) { + protected void finish(Uri result) { if (result != null) { - onTaskFinished(result); + mOwner.onTaskFinished(result); } else { Snackbars.makeSnackbar( - DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show(); + mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show(); } - setPending(false); + mOwner.setPending(false); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java index 89be9107e077..d589d5e0e237 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java @@ -24,8 +24,6 @@ import android.app.Fragment; import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ClipData; -import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -37,10 +35,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toolbar; -import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; -import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; @@ -72,23 +68,19 @@ public class DownloadsActivity extends BaseActivity { // talkback from reading aloud the default title, we clear it here. setTitle(""); final Uri rootUri = getIntent().getData(); - new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); + new RestoreRootTask(this, rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override - State buildState() { - State state = buildDefaultState(); - + void includeState(State state) { state.action = ACTION_MANAGE; state.acceptMimes = new String[] { "*/*" }; state.allowMultiple = true; state.showSize = true; state.excludedAuthorities = getExcludedAuthorities(); - - return state; } @Override @@ -108,14 +100,12 @@ public class DownloadsActivity extends BaseActivity { final MenuItem advanced = menu.findItem(R.id.menu_advanced); final MenuItem createDir = menu.findItem(R.id.menu_create_dir); - final MenuItem newWindow = menu.findItem(R.id.menu_new_window); final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard); final MenuItem fileSize = menu.findItem(R.id.menu_file_size); advanced.setVisible(false); createDir.setVisible(false); pasteFromCb.setEnabled(false); - newWindow.setEnabled(false); fileSize.setVisible(false); Menus.disableHiddenItems(menu); @@ -170,21 +160,6 @@ public class DownloadsActivity extends BaseActivity { public void onDocumentsPicked(List<DocumentInfo> docs) {} @Override - void saveStackBlocking() { - final ContentResolver resolver = getContentResolver(); - final ContentValues values = new ContentValues(); - - final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); - - // Remember location for next app launch - final String packageName = getCallingPackageMaybeExtra(); - values.clear(); - values.put(ResumeColumns.STACK, rawStack); - values.put(ResumeColumns.EXTERNAL, 0); - resolver.insert(RecentsProvider.buildResume(packageName), values); - } - - @Override void onTaskFinished(Uri... uris) { Log.d(TAG, "onFinished() " + Arrays.toString(uris)); diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index 3aba356793df..c81f342822f6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -30,7 +30,6 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; @@ -98,19 +97,19 @@ public class FilesActivity extends BaseActivity { refreshCurrentRootAndDirectory(ANIM_NONE); } else if (intent.getAction() == Intent.ACTION_VIEW) { checkArgument(uri != null); - new OpenUriForViewTask().executeOnExecutor( + new OpenUriForViewTask(this).executeOnExecutor( ProviderExecutor.forAuthority(uri.getAuthority()), uri); } else if (DocumentsContract.isRootUri(this, uri)) { if (DEBUG) Log.d(TAG, "Launching with root URI."); // If we've got a specific root to display, restore that root using a dedicated // authority. That way a misbehaving provider won't result in an ANR. - new RestoreRootTask(uri).executeOnExecutor( + new RestoreRootTask(this, uri).executeOnExecutor( ProviderExecutor.forAuthority(uri.getAuthority())); } else { if (DEBUG) Log.d(TAG, "Launching into Home directory."); // If all else fails, try to load "Home" directory. final Uri homeUri = DocumentsContract.buildHomeUri(); - new RestoreRootTask(homeUri).executeOnExecutor( + new RestoreRootTask(this, homeUri).executeOnExecutor( ProviderExecutor.forAuthority(homeUri.getAuthority())); } @@ -134,9 +133,7 @@ public class FilesActivity extends BaseActivity { } @Override - State buildState() { - State state = buildDefaultState(); - + void includeState(State state) { final Intent intent = getIntent(); state.action = State.ACTION_BROWSE; @@ -149,8 +146,6 @@ public class FilesActivity extends BaseActivity { if (stack != null) { state.stack = stack; } - - return state; } @Override @@ -190,14 +185,6 @@ public class FilesActivity extends BaseActivity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { - boolean showMenu = super.onCreateOptionsMenu(menu); - - expandMenus(menu); - return showMenu; - } - - @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); @@ -206,15 +193,13 @@ public class FilesActivity extends BaseActivity { final MenuItem createDir = menu.findItem(R.id.menu_create_dir); final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard); final MenuItem settings = menu.findItem(R.id.menu_settings); + final MenuItem newWindow = menu.findItem(R.id.menu_new_window); createDir.setVisible(true); createDir.setEnabled(canCreateDirectory()); pasteFromCb.setEnabled(mClipper.hasItemsToPaste()); settings.setVisible(root.hasSettings()); - - // TODO: For some reason settings menu item is not - // honoring the "showAsAction=never" setting in activity.xml. - settings.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + newWindow.setVisible(true); Menus.disableHiddenItems(menu, pasteFromCb); return true; @@ -363,13 +348,15 @@ public class FilesActivity extends BaseActivity { } } - @Override - void saveStackBlocking() { + // Turns out only DocumentsActivity was ever calling saveStackBlocking. + // There may be a case where we want to contribute entries from + // Behavior here in FilesActivity, but it isn't yet obvious. + // TODO: Contribute to recents, or remove this. + void writeStackToRecentsBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); - final byte[] rawStack = DurableUtils.writeToArrayOrNull( - getDisplayState().stack); + final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); // Remember location for next app launch final String packageName = getCallingPackageMaybeExtra(); @@ -408,12 +395,19 @@ public class FilesActivity extends BaseActivity { * to know which root to select. Also, the stack doesn't contain intermediate directories. * It's primarly used for opening ZIP archives from Downloads app. */ - final class OpenUriForViewTask extends AsyncTask<Uri, Void, Void> { + private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> { + + private final State mState; + public OpenUriForViewTask(FilesActivity activity) { + super(activity); + mState = activity.mState; + } + @Override - protected Void doInBackground(Uri... params) { + protected Void run(Uri... params) { final Uri uri = params[0]; - final RootsCache rootsCache = DocumentsApplication.getRootsCache(FilesActivity.this); + final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner); final String authority = uri.getAuthority(); final Collection<RootInfo> roots = @@ -426,20 +420,17 @@ public class FilesActivity extends BaseActivity { final RootInfo root = roots.iterator().next(); mState.stack.root = root; try { - mState.stack.add(DocumentInfo.fromUri(getContentResolver(), uri)); + mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri)); } catch (FileNotFoundException e) { Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri); } - mState.stack.add(getRootDocumentBlocking(root)); + mState.stack.add(mOwner.getRootDocumentBlocking(root)); return null; } @Override - protected void onPostExecute(Void result) { - if (isDestroyed()) { - return; - } - refreshCurrentRootAndDirectory(ANIM_NONE); + protected void finish(Void result) { + mOwner.refreshCurrentRootAndDirectory(ANIM_NONE); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java b/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java new file mode 100644 index 000000000000..b74acb8e38c3 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.app.Activity; +import android.os.AsyncTask; + +/** + * An {@link AsyncTask} that guards work with checks that a paired {@link Activity} + * is still alive. Instances of this class make no progress. + * + * <p>Use this type of task for greater safety when executing tasks that might complete + * after an Activity is destroyed. + * + * <p>Also useful as tasks can be static, limiting scope, but still have access to + * the owning class (by way the A template and the mActivity field). + * + * @template Owner Activity type. + * @template Input input type + * @template Output output type + */ +abstract class PairedTask<Owner extends Activity, Input, Output> + extends AsyncTask<Input, Void, Output> { + + protected final Owner mOwner; + + public PairedTask(Owner owner) { + mOwner = owner; + } + + /** Called prior to run being executed. Analogous to {@link AsyncTask#onPreExecute} */ + void prepare() {} + + /** Analogous to {@link AsyncTask#doInBackground} */ + abstract Output run(Input... input); + + /** Analogous to {@link AsyncTask#onPostExecute} */ + abstract void finish(Output output); + + @Override + final protected void onPreExecute() { + if (mOwner.isDestroyed()) { + return; + } + prepare(); + } + + @Override + final protected Output doInBackground(Input... input) { + if (mOwner.isDestroyed()) { + return null; + } + return run(input); + } + + @Override + final protected void onPostExecute(Output result) { + if (mOwner.isDestroyed()) { + return; + } + finish(result); + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java b/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java new file mode 100644 index 000000000000..9048b9d45ee7 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.net.Uri; +import android.provider.DocumentsContract; +import android.util.Log; + +import com.android.documentsui.model.RootInfo; + +final class RestoreRootTask extends PairedTask<BaseActivity, Void, RootInfo> { + private static final String TAG = "RestoreRootTask"; + + private final Uri mRootUri; + + public RestoreRootTask(BaseActivity activity, Uri rootUri) { + super(activity); + mRootUri = rootUri; + } + + @Override + protected RootInfo run(Void... params) { + String rootId = DocumentsContract.getRootId(mRootUri); + return mOwner.mRoots.getRootOneshot(mRootUri.getAuthority(), rootId); + } + + @Override + protected void finish(RootInfo root) { + mOwner.mState.restored = true; + + if (root != null) { + mOwner.onRootPicked(root); + } else { + Log.w(TAG, "Failed to find root: " + mRootUri); + mOwner.finish(); + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java index 28f743243234..d141de667f2f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/State.java +++ b/packages/DocumentsUI/src/com/android/documentsui/State.java @@ -158,9 +158,14 @@ public class State implements android.os.Parcelable { out.writeInt(mStackTouched ? 1 : 0); } - public static final Creator<State> CREATOR = new Creator<State>() { + public static final ClassLoaderCreator<State> CREATOR = new ClassLoaderCreator<State>() { @Override public State createFromParcel(Parcel in) { + return createFromParcel(in, null); + } + + @Override + public State createFromParcel(Parcel in, ClassLoader loader) { final State state = new State(); state.action = in.readInt(); state.acceptMimes = in.readStringArray(); @@ -174,9 +179,9 @@ public class State implements android.os.Parcelable { state.restored = in.readInt() != 0; DurableUtils.readFromParcel(in, state.stack); state.currentSearch = in.readString(); - in.readMap(state.dirState, null); - in.readList(state.selectedDocumentsForCopy, null); - in.readList(state.excludedAuthorities, null); + in.readMap(state.dirState, loader); + in.readList(state.selectedDocumentsForCopy, loader); + in.readList(state.excludedAuthorities, loader); state.openableOnly = in.readInt() != 0; state.mStackTouched = in.readInt() != 0; return state; diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 1e3da6b1ac8a..70bee3cd3c6e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -100,7 +100,6 @@ import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperations; - import com.google.common.collect.Lists; import java.util.ArrayList; @@ -854,27 +853,28 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi } private void showEmptyDirectory() { - showEmptyView(R.string.empty); + showEmptyView(R.string.empty, R.drawable.cabinet); } private void showNoResults(RootInfo root) { CharSequence msg = getContext().getResources().getText(R.string.no_results); - showEmptyView(String.format(String.valueOf(msg), root.title)); + showEmptyView(String.format(String.valueOf(msg), root.title), R.drawable.cabinet); } - // Shows an error indicating documents couldn't be queried. private void showQueryError() { - showEmptyView(R.string.query_error); + showEmptyView(R.string.query_error, R.drawable.hourglass); } - private void showEmptyView(@StringRes int id) { - showEmptyView(getContext().getResources().getText(id)); + private void showEmptyView(@StringRes int id, int drawable) { + showEmptyView(getContext().getResources().getText(id), drawable); } - private void showEmptyView(CharSequence msg) { + private void showEmptyView(CharSequence msg, int drawable) { View content = mEmptyView.findViewById(R.id.content); TextView msgView = (TextView) mEmptyView.findViewById(R.id.message); + ImageView imageView = (ImageView) mEmptyView.findViewById(R.id.artwork); msgView.setText(msg); + imageView.setImageResource(drawable); content.animate().cancel(); // cancel any ongoing animations diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java index 1829746bb143..ef1e8e20aaff 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java @@ -24,9 +24,11 @@ import android.net.Uri; import android.os.Bundle; import android.os.Process; import android.provider.DocumentsContract; +import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; @@ -56,11 +58,17 @@ class DocumentLoader { private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles) throws IOException { - final MtpObjectInfo[] objectInfos = new MtpObjectInfo[handles.length]; + final ArrayList<MtpObjectInfo> objects = new ArrayList<>(); for (int i = 0; i < handles.length; i++) { - objectInfos[i] = manager.getObjectInfo(deviceId, handles[i]); + final MtpObjectInfo info = manager.getObjectInfo(deviceId, handles[i]); + if (info == null) { + Log.e(MtpDocumentsProvider.TAG, + "Failed to obtain object info handle=" + handles[i]); + continue; + } + objects.add(info); } - return objectInfos; + return objects.toArray(new MtpObjectInfo[objects.size()]); } synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent) diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index b44f9af3c3bc..8a3ebefa127f 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -577,9 +577,7 @@ class MtpDatabase { static void getObjectDocumentValues( ContentValues values, int deviceId, String parentId, MtpObjectInfo info) { values.clear(); - final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ? - DocumentsContract.Document.MIME_TYPE_DIR : - MediaFile.getMimeTypeForFormatCode(info.getFormat()); + final String mimeType = getMimeType(info); int flag = 0; if (info.getProtectionStatus() == 0) { flag |= Document.FLAG_SUPPORTS_DELETE | @@ -608,6 +606,17 @@ class MtpDatabase { values.put(Document.COLUMN_SIZE, info.getCompressedSize()); } + private static String getMimeType(MtpObjectInfo info) { + if (info.getFormat() == MtpConstants.FORMAT_ASSOCIATION) { + return DocumentsContract.Document.MIME_TYPE_DIR; + } + final String formatCodeMimeType = MediaFile.getMimeTypeForFormatCode(info.getFormat()); + if (formatCodeMimeType != null) { + return formatCodeMimeType; + } + return MediaFile.getMimeTypeForFile(info.getName()); + } + static String[] strings(Object... args) { final String[] results = new String[args.length]; for (int i = 0; i < args.length; i++) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 70a1aae86b95..033845401b4b 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -184,6 +184,9 @@ public class MtpDocumentsProvider extends DocumentsProvider { public ParcelFileDescriptor openDocument( String documentId, String mode, CancellationSignal signal) throws FileNotFoundException { + if (DEBUG) { + Log.d(TAG, "openDocument: " + documentId); + } final Identifier identifier = mDatabase.createIdentifier(documentId); try { openDevice(identifier.mDeviceId); @@ -270,6 +273,9 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException { + if (DEBUG) { + Log.d(TAG, "createDocument: " + displayName); + } try { final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); openDevice(parentId.mDeviceId); diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index 78c530c2b0d7..f8851101abc2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -155,7 +155,7 @@ public class RestrictedLockUtils { for (UserInfo userInfo : um.getProfiles(userId)) { final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); if (admins == null) { - return null; + continue; } final boolean isSeparateProfileChallengeEnabled = lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); @@ -209,16 +209,7 @@ public class RestrictedLockUtils { IPackageManager ipm = AppGlobals.getPackageManager(); try { if (ipm.getBlockUninstallForUser(packageName, userId)) { - DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( - Context.DEVICE_POLICY_SERVICE); - if (dpm == null) { - return null; - } - ComponentName admin = dpm.getProfileOwner(); - if (admin == null) { - admin = dpm.getDeviceOwnerComponentOnCallingUser(); - } - return new EnforcedAdmin(admin, UserHandle.myUserId()); + return getProfileOrDeviceOwner(context, userId); } } catch (RemoteException e) { // Nothing to do @@ -238,7 +229,7 @@ public class RestrictedLockUtils { try { ApplicationInfo ai = ipm.getApplicationInfo(packageName, 0, userId); if (ai != null && ((ai.flags & ApplicationInfo.FLAG_SUSPENDED) != 0)) { - return getProfileOrDeviceOwnerOnCallingUser(context); + return getProfileOrDeviceOwner(context, userId); } } catch (RemoteException e) { // Nothing to do @@ -246,6 +237,80 @@ public class RestrictedLockUtils { return null; } + public static EnforcedAdmin checkIfInputMethodDisallowed(Context context, + String packageName, int userId) { + DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } + EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); + boolean permitted = true; + if (admin != null) { + permitted = dpm.isInputMethodPermittedByAdmin(admin.component, + packageName, userId); + } + int managedProfileId = getManagedProfileId(context, userId); + EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); + boolean permittedByProfileAdmin = true; + if (profileAdmin != null) { + permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component, + packageName, managedProfileId); + } + if (!permitted && !permittedByProfileAdmin) { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } else if (!permitted) { + return admin; + } else if (!permittedByProfileAdmin) { + return profileAdmin; + } + return null; + } + + public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context, + String packageName, int userId) { + DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } + EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); + boolean permitted = true; + if (admin != null) { + permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component, + packageName, userId); + } + int managedProfileId = getManagedProfileId(context, userId); + EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); + boolean permittedByProfileAdmin = true; + if (profileAdmin != null) { + permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin( + profileAdmin.component, packageName, managedProfileId); + } + if (!permitted && !permittedByProfileAdmin) { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } else if (!permitted) { + return admin; + } else if (!permittedByProfileAdmin) { + return profileAdmin; + } + return null; + } + + private static int getManagedProfileId(Context context, int userId) { + UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + List<UserInfo> userProfiles = um.getProfiles(userId); + for (UserInfo uInfo : userProfiles) { + if (uInfo.id == userId) { + continue; + } + if (uInfo.isManagedProfile()) { + return uInfo.id; + } + } + return UserHandle.USER_NULL; + } + /** * Check if account management for a specific type of account is disabled by admin. * Only a profile or device owner can disable account management. So, we check if account @@ -255,7 +320,7 @@ public class RestrictedLockUtils { * or {@code null} if the account management is not disabled. */ public static EnforcedAdmin checkIfAccountManagementDisabled(Context context, - String accountType) { + String accountType, int userId) { if (accountType == null) { return null; } @@ -265,7 +330,7 @@ public class RestrictedLockUtils { return null; } boolean isAccountTypeDisabled = false; - String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled(); + String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId); for (String type : disabledTypes) { if (accountType.equals(type)) { isAccountTypeDisabled = true; @@ -275,7 +340,7 @@ public class RestrictedLockUtils { if (!isAccountTypeDisabled) { return null; } - return getProfileOrDeviceOwnerOnCallingUser(context); + return getProfileOrDeviceOwner(context, userId); } /** @@ -296,7 +361,7 @@ public class RestrictedLockUtils { } /** - * Checks if an admin has enforced minimum password quality requirements on the device. + * Checks if an admin has enforced minimum password quality requirements on the given user. * * @return EnforcedAdmin Object containing the enforced admin component and admin user details, * or {@code null} if no quality requirements are set. If the requirements are set by @@ -304,35 +369,73 @@ public class RestrictedLockUtils { * {@link UserHandle#USER_NULL}. * */ - public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) { + public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm == null) { return null; } - boolean isDisabledByMultipleAdmins = false; - ComponentName adminComponent = null; - List<ComponentName> admins = dpm.getActiveAdmins(); - int quality; - if (admins != null) { + + LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + EnforcedAdmin enforcedAdmin = null; + if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { + // userId is managed profile and has a separate challenge, only consider + // the admins in that user. + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); + if (admins == null) { + return null; + } for (ComponentName admin : admins) { - quality = dpm.getPasswordQuality(admin); - if (quality >= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - if (adminComponent == null) { - adminComponent = admin; + if (dpm.getPasswordQuality(admin, userId) + > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userId); } else { - isDisabledByMultipleAdmins = true; - break; + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } } } - } - EnforcedAdmin enforcedAdmin = null; - if (adminComponent != null) { - if (!isDisabledByMultipleAdmins) { - enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId()); - } else { - enforcedAdmin = new EnforcedAdmin(); + } else { + // Return all admins for this user and the profiles that are visible from this + // user that do not use a separate work challenge. + final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + for (UserInfo userInfo : um.getProfiles(userId)) { + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); + if (admins == null) { + continue; + } + final boolean isSeparateProfileChallengeEnabled = + lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); + for (ComponentName admin : admins) { + if (!isSeparateProfileChallengeEnabled) { + if (dpm.getPasswordQuality(admin, userInfo.id) + > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + // This same admins could have set policies both on the managed profile + // and on the parent. So, if the admin has set the policy on the + // managed profile here, we don't need to further check if that admin + // has set policy on the parent admin. + continue; + } + } + if (userInfo.isManagedProfile()) { + // If userInfo.id is a managed profile, we also need to look at + // the policies set on the parent. + DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo); + if (parentDpm.getPasswordQuality(admin, userInfo.id) + > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + } + } + } } } return enforcedAdmin; @@ -352,7 +455,8 @@ public class RestrictedLockUtils { EnforcedAdmin enforcedAdmin = null; final int userId = UserHandle.myUserId(); if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { - // If the user has a separate challenge, only consider the admins in that user. + // userId is managed profile and has a separate challenge, only consider + // the admins in that user. final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); if (admins == null) { return null; @@ -373,7 +477,7 @@ public class RestrictedLockUtils { for (UserInfo userInfo : um.getProfiles(userId)) { final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); if (admins == null) { - return null; + continue; } final boolean isSeparateProfileChallengeEnabled = lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); @@ -410,19 +514,24 @@ public class RestrictedLockUtils { return enforcedAdmin; } - public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) { + public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) { + if (userId == UserHandle.USER_NULL) { + return null; + } final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm == null) { return null; } - ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); + ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); if (adminComponent != null) { - return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); + return new EnforcedAdmin(adminComponent, userId); } - adminComponent = dpm.getProfileOwner(); - if (adminComponent != null) { - return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); + if (dpm.getDeviceOwnerUserId() == userId) { + adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); + if (adminComponent != null) { + return new EnforcedAdmin(adminComponent, userId); + } } return null; } diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java index f5a2aaec97d7..d368de93e263 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -15,40 +15,19 @@ */ package com.android.settingslib; -import android.app.ActivityManager; -import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.SystemProperties; -import android.os.UserManager; -import android.provider.Settings; import android.telephony.CarrierConfigManager; public class TetherUtil { - // Extras used for communicating with the TetherService. - public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; - public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - /** - * Tells the service to run a provision check now. - */ - public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - public static boolean setWifiTethering(boolean enable, Context context) { final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); return wifiManager.setWifiApEnabled(null, enable); } - public static boolean isWifiTetherEnabled(Context context) { - WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - return wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED; - } - private static boolean isEntitlementCheckRequired(Context context) { final CarrierConfigManager configManager = (CarrierConfigManager) context .getSystemService(Context.CARRIER_CONFIG_SERVICE); @@ -71,13 +50,4 @@ public class TetherUtil { } return (provisionApp.length == 2); } - - public static boolean isTetheringSupported(Context context) { - final ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - final boolean isAdminUser = - UserManager.get(context).isUserAdmin(ActivityManager.getCurrentUser()); - return isAdminUser && cm.isTetheringSupported(); - } - } diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml deleted file mode 100644 index 10ddce68dcaf..000000000000 --- a/packages/SystemUI/res/anim/recents_from_app_enter.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> -<!-- Recents Activity --> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="top"> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/fast_out_slow_in" - android:duration="0"/> -</set> diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml deleted file mode 100644 index c98ecf43eb65..000000000000 --- a/packages/SystemUI/res/anim/recents_from_app_exit.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> -<!-- Incoming Activity --> -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="normal"> - - <!-- Animate the view out only after recents is visible --> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/fast_out_slow_in" - android:duration="1"/> -</set> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml index b191e625177b..00b3dfda135e 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/linear" + android:interpolator="@android:interpolator/linear_out_slow_in" android:duration="150"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml index fa6caf295cce..33831b8c0a32 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="150"/> + android:interpolator="@interpolator/recents_from_launcher_exit_interpolator" + android:duration="133"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml index e0e2fc83a816..23cedf85adf6 100644 --- a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="@integer/recents_enter_from_home_transition_duration"/> + android:interpolator="@interpolator/recents_from_launcher_exit_interpolator" + android:duration="133"/> </set> diff --git a/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml deleted file mode 100644 index 1135bc097fcf..000000000000 --- a/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="normal"> - <!--scale android:fromXScale="2.0" android:toXScale="1.0" - android:fromYScale="2.0" android:toYScale="1.0" - android:interpolator="@android:interpolator/decelerate_cubic" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="50%p" - android:duration="250" /--> -</set> diff --git a/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml deleted file mode 100644 index e95e667507f3..000000000000 --- a/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="normal"> - <!--scale android:fromXScale="1.0" android:toXScale="2.0" - android:fromYScale="1.0" android:toYScale="2.0" - android:interpolator="@android:interpolator/decelerate_cubic" - android:pivotX="50%p" android:pivotY="50%p" - android:duration="250" /--> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> -</set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml index b191e625177b..544ec88d2bfa 100644 --- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/linear" - android:duration="150"/> + android:interpolator="@interpolator/recents_to_launcher_enter_interpolator" + android:duration="133"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml index fa6caf295cce..226edb85d049 100644 --- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml @@ -24,5 +24,5 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="150"/> + android:duration="1"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml index ea8283511b96..657b2164d6f2 100644 --- a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/linear" - android:duration="100"/> + android:interpolator="@interpolator/recents_to_launcher_enter_interpolator" + android:duration="133"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml index a8bdc8e45f59..5182cab29ee4 100644 --- a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml @@ -24,5 +24,5 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear" - android:duration="100"/> + android:duration="1"/> </set> diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml deleted file mode 100644 index 73ae9f2a25ee..000000000000 --- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:detachWallpaper="true" - android:shareInterpolator="false" - android:zAdjustment="normal"> - <!--scale android:fromXScale="2.0" android:toXScale="1.0" - android:fromYScale="2.0" android:toYScale="1.0" - android:interpolator="@android:interpolator/decelerate_cubic" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="50%p" - android:duration="250" /--> - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> -</set> diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml deleted file mode 100644 index 7e257d938e71..000000000000 --- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:detachWallpaper="true" - android:shareInterpolator="false" - android:zAdjustment="top"> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> -</set> diff --git a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml b/packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml index efa901915886..4a7fff67eac5 100644 --- a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml +++ b/packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* -** Copyright 2012, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -16,11 +16,8 @@ ** limitations under the License. */ --> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="normal"> - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> -</set> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0" + android:controlY1="0" + android:controlX2="0.8" + android:controlY2="1" /> diff --git a/packages/SystemUI/res/anim/recents_launch_from_launcher_exit.xml b/packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml index fa28cf42eee9..c61dfd87d842 100644 --- a/packages/SystemUI/res/anim/recents_launch_from_launcher_exit.xml +++ b/packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* -** Copyright 2012, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -16,13 +16,8 @@ ** limitations under the License. */ --> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="top"> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> -</set> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.4" + android:controlY1="0" + android:controlX2="1" + android:controlY2="1" /> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml index 802acfed73e8..778ef8ffd3dd 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml @@ -15,8 +15,11 @@ ~ limitations under the License --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> -</LinearLayout> +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_marginStart="24dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="0dp" + android:layout_marginBottom="20dp" + android:background="?android:attr/dividerHorizontal" /> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml index fa07eb1b105c..7ca3b954fcf9 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml @@ -16,7 +16,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content"> -</LinearLayout> + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml index 77b12641c8c6..f73ee1532ba3 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml @@ -14,25 +14,35 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/keyboard_shortcuts_wrapper" - android:layout_width="488dp" + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="488dp" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:focusable="true"> - <ScrollView + android:orientation="vertical"> + <ScrollView android:id="@+id/keyboard_shortcuts_scroll_view" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"> - <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <LinearLayout android:id="@+id/keyboard_shortcuts_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"/> - </ScrollView> - <View + </ScrollView> + <!-- Required for stretching to full available height when the items in the scroll view + occupy less space then the full height --> + <View android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/listDivider"/> -</LinearLayout> + android:layout_height="0dp" + android:layout_weight="1"/> + </LinearLayout> + <View + android:layout_gravity="bottom" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/dividerHorizontal"/> +</FrameLayout> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 9bb6dc6abb75..a9b8df2934c7 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -158,5 +158,4 @@ <!-- Keyboard shortcuts colors --> <color name="ksh_system_group_color">#ff00bcd4</color> <color name="ksh_application_group_color">#fff44336</color> - <color name="ksh_dialog_background_color">#ffffffff</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index e8df01b19415..a6ba8b59303d 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -103,7 +103,7 @@ <!-- The default tiles to display in QuickSettings --> <string name="quick_settings_tiles_default" translatable="false"> - wifi,bt,flashlight,dnd,cell,battery,rotation,airplane,location,cast,work + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location </string> <!-- The tiles to display in QuickSettings --> @@ -138,12 +138,6 @@ <!-- The duration in seconds to wait before the dismiss buttons are shown. --> <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer> - <!-- The duration of the window transition when coming to Recents from an app. - In order to defer the in-app animations until after the transition is complete, - we also need to use this value as the starting delay when animating the first - task decorations in. --> - <integer name="recents_enter_from_app_transition_duration">325</integer> - <!-- The duration for animating the task decorations in after transitioning from an app. --> <integer name="recents_task_enter_from_app_duration">200</integer> @@ -153,12 +147,6 @@ <!-- The duration for animating the task decorations out before transitioning to an app. --> <integer name="recents_task_exit_to_app_duration">125</integer> - <!-- The duration of the window transition when coming to Recents from the Launcher. - In order to defer the in-app animations until after the transition is complete, - we also need to use this value as the starting delay when animating the task views - in from the bottom of the screen. --> - <integer name="recents_enter_from_home_transition_duration">100</integer> - <!-- The min animation duration for animating the nav bar scrim in. --> <integer name="recents_nav_bar_scrim_enter_duration">400</integer> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 76752e633f4a..6ff9be14b3ce 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1184,6 +1184,11 @@ <!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE --> <string name="overview_initial_state_paging_desc">Determines whether Overview will initially be in a stacked or paged state</string> + <!-- Toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=60]--> + <string name="overview_nav_bar_gesture">Enable split-screen swipe-up accelerator</string> + <!-- Description for the toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=NONE]--> + <string name="overview_nav_bar_gesture_desc">Enable gesture to enter split-screen by swiping up from the Overview button</string> + <!-- Category in the System UI Tuner settings, where new/experimental settings are --> <string name="experimental">Experimental</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index b078a68ff35a..60a9fc233aa3 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -16,10 +16,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="RecentsStyle" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar"> - <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item> - </style> - <style name="RecentsTheme" parent="@android:style/Theme.Material"> <!-- NoTitle --> <item name="android:windowNoTitle">true</item> @@ -27,38 +23,23 @@ <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> - <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item> + <item name="android:windowAnimationStyle">@null</item> <item name="android:ambientShadowAlpha">0.35</item> </style> - <!-- Alternate Recents theme --> + <!-- Recents theme --> <style name="RecentsTheme.Wallpaper"> - <!-- Wallpaper --> <item name="android:windowBackground">@color/transparent</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowShowWallpaper">true</item> </style> - <!-- Performance optimized alternate Recents theme (no wallpaper) --> + <!-- Performance optimized Recents theme (no wallpaper) --> <style name="RecentsTheme.NoWallpaper"> <item name="android:windowBackground">@android:color/black</item> </style> - <!-- Animations for a non-full-screen window or activity. --> - <style name="Animation.RecentsActivity" parent="@android:style/Animation.Activity"> - <item name="android:activityOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item> - <item name="android:activityOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item> - <item name="android:taskOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item> - <item name="android:taskOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item> - <item name="android:taskToFrontEnterAnimation">@anim/recents_launch_from_launcher_enter</item> - <item name="android:taskToFrontExitAnimation">@anim/recents_launch_from_launcher_exit</item> - <item name="android:wallpaperOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item> - <item name="android:wallpaperOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item> - <item name="android:wallpaperIntraOpenEnterAnimation">@anim/wallpaper_recents_launch_from_launcher_enter</item> - <item name="android:wallpaperIntraOpenExitAnimation">@anim/wallpaper_recents_launch_from_launcher_exit</item> - </style> - <style name="TextAppearance.StatusBar.HeadsUp" parent="@*android:style/TextAppearance.StatusBar"> </style> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index febe5188cf88..4de4cede0faa 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -122,6 +122,11 @@ android:title="@string/overview_fast_toggle_via_button" android:summary="@string/overview_fast_toggle_via_button_desc" /> + <com.android.systemui.tuner.TunerSwitch + android:key="overview_nav_bar_gesture" + android:title="@string/overview_nav_bar_gesture" + android:summary="@string/overview_nav_bar_gesture_desc" /> + </PreferenceScreen> <SwitchPreference diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java index e770b5d89a0b..454d1ceb4b04 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java @@ -182,6 +182,7 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode, mListening = true; mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); + updateShowPercent(); if (mDemoMode) return; mBatteryController.addStateChangedCallback(this); } @@ -193,6 +194,11 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode, mBatteryController.removeStateChangedCallback(this); } + public void disableShowPercent() { + mShowPercent = false; + postInvalidate(); + } + private void postInvalidate() { mHandler.post(new Runnable() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java index cd6dce01e48e..5e33a9f84cac 100644 --- a/packages/SystemUI/src/com/android/systemui/Interpolators.java +++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java @@ -34,4 +34,10 @@ public class Interpolators { public static final Interpolator LINEAR = new LinearInterpolator(); public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); + + /** + * Interpolator to be used when animating a move based on a click. Pair with enough duration. + */ + public static final Interpolator TOUCH_RESPONSE = + new PathInterpolator(0.3f, 0f, 0.1f, 1f); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 6840e08f2cfe..29f8af21e3bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -42,6 +42,7 @@ public class TileQueryHelper { private static final String TAG = "TileQueryHelper"; private final ArrayList<TileInfo> mTiles = new ArrayList<>(); + private final ArrayList<String> mSpecs = new ArrayList<>(); private final Context mContext; private TileStateListener mListener; @@ -76,7 +77,7 @@ public class TileQueryHelper { mainHandler.post(new Runnable() { @Override public void run() { - addTile(spec, state, mTiles); + addTile(spec, state); mListener.onTilesChanged(mTiles); } }); @@ -95,20 +96,23 @@ public class TileQueryHelper { mListener = listener; } - private static void addTile(String spec, QSTile.State state, List<TileInfo> tiles) { + private void addTile(String spec, QSTile.State state) { + if (mSpecs.contains(spec)) { + return; + } TileInfo info = new TileInfo(); info.state = state; info.spec = spec; - tiles.add(info); + mTiles.add(info); + mSpecs.add(spec); } - private static void addTile(String spec, Drawable drawable, CharSequence label, Context context, - List<TileInfo> tiles) { + private void addTile(String spec, Drawable drawable, CharSequence label, Context context) { QSTile.State state = new QSTile.State(); state.label = label; state.contentDescription = label; state.icon = new DrawableIcon(drawable); - addTile(spec, state, tiles); + addTile(spec, state); } public static class TileInfo { @@ -133,7 +137,7 @@ public class TileQueryHelper { icon.setTint(mContext.getColor(android.R.color.white)); } CharSequence label = info.serviceInfo.loadLabel(pm); - addTile(spec, icon, label != null ? label.toString() : "null", mContext, tiles); + addTile(spec, icon, label != null ? label.toString() : "null", mContext); } return tiles; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java index cd3e3b2a3bf0..72cdf180f999 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java @@ -153,6 +153,7 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll private void bindView() { mDrawable.onBatteryLevelChanged(100, false, false); mDrawable.onPowerSaveChanged(true); + mDrawable.disableShowPercent(); ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable); Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle); checkbox.setChecked(mPowerSave); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index d01a288d6fcf..3f482c82011a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -45,7 +45,6 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; -import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent; @@ -130,6 +129,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD */ public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) { mLaunchIntent = launchIntent; + mOpts = opts; } @Override @@ -437,11 +437,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD protected void onPause() { super.onPause(); - RecentsDebugFlags flags = Recents.getDebugFlags(); - if (flags.isFastToggleRecentsEnabled()) { - // Stop the fast-toggle dozer - mIterateTrigger.stopDozing(); - } + // Stop the fast-toggle dozer + mIterateTrigger.stopDozing(); } @Override @@ -648,6 +645,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } public final void onBusEvent(UserInteractionEvent event) { + // Stop the fast-toggle dozer mIterateTrigger.stopDozing(); } @@ -694,21 +692,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } } - public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) { - RecentsDebugFlags debugFlags = Recents.getDebugFlags(); - RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); - if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() && - RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) { - mIterateTrigger.setDozeDuration( - getResources().getInteger(R.integer.recents_auto_advance_duration)); - if (!mIterateTrigger.isDozing()) { - mIterateTrigger.startDozing(); - } else { - mIterateTrigger.poke(); - } - } - } - public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) { EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true)); mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index 0afa1f6a495f..177e8417f3fb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -52,10 +52,23 @@ public class RecentsActivityLaunchState { * Returns the task to focus given the current launch state. */ public int getInitialFocusTaskIndex(int numTasks) { + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); if (launchedFromAppWithThumbnail) { + if (debugFlags.isFastToggleRecentsEnabled()) { + // If fast toggling, focus the front most task so that the next tap will focus the + // N-1 task + return numTasks - 1; + } + // If coming from another app, focus the next task return numTasks - 2; } else { + if (debugFlags.isFastToggleRecentsEnabled()) { + // If fast toggling, defer focusing until the next tap (which will automatically + // focus the front most task) + return -1; + } + // If coming from home, focus the first task return numTasks - 1; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index 6b8968f02a11..fc14758af0f4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -39,8 +39,6 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static final boolean EnableAffiliatedTaskGroups = true; // Overrides the Tuner flags and enables the fast toggle and timeout public static final boolean EnableFastToggleTimeoutOverride = true; - // Enables toggling the fast-toggle timeout immediately after entering Recents - public static final boolean EnableFastToggleTimeoutOnEnter = true; // Enables us to create mock recents tasks public static final boolean EnableMockTasks = false; @@ -90,9 +88,6 @@ public class RecentsDebugFlags implements TunerService.Tunable { * @return whether the initial stack state is paging. */ public boolean isInitialStatePaging() { - if (Static.EnableFastToggleTimeoutOnEnter) { - return true; - } return mInitialStatePaging; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 1cceef4484fe..dd7b7c1b89e6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -38,6 +38,7 @@ import android.util.MutableBoolean; import android.view.AppTransitionAnimationSpec; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; @@ -48,6 +49,7 @@ import com.android.systemui.recents.events.activity.DockingTopTaskEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; +import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; @@ -81,8 +83,10 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener { private final static String TAG = "RecentsImpl"; + // The minimum amount of time between each recents button press that we will handle private final static int MIN_TOGGLE_DELAY_MS = 350; + // The duration within which the user releasing the alt tab (from when they pressed alt tab) // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this // duration, then we will toggle recents after this duration. @@ -337,21 +341,31 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener mTriggeredFromAltTab = false; try { + ViewConfiguration viewConfig = ViewConfiguration.get(mContext); SystemServicesProxy ssp = Recents.getSystemServices(); ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask(); MutableBoolean isTopTaskHome = new MutableBoolean(true); + long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime; + if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) { RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); if (!launchState.launchedWithAltTab) { - // Notify recents to move onto the next task - EventBus.getDefault().post(new IterateRecentsEvent()); + // If the user taps quickly + if (ViewConfiguration.getDoubleTapMinTime() < elapsedTime && + elapsedTime < ViewConfiguration.getDoubleTapTimeout()) { + // Launch the next focused task + EventBus.getDefault().post(new LaunchNextTaskRequestEvent()); + } else { + // Notify recents to move onto the next task + EventBus.getDefault().post(new IterateRecentsEvent()); + } } else { // If the user has toggled it too quickly, then just eat up the event here (it's // better than showing a janky screenshot). // NOTE: Ideally, the screenshot mechanism would take the window transform into // account - if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) { + if (elapsedTime < MIN_TOGGLE_DELAY_MS) { return; } @@ -364,7 +378,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // better than showing a janky screenshot). // NOTE: Ideally, the screenshot mechanism would take the window transform into // account - if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) { + if (elapsedTime < MIN_TOGGLE_DELAY_MS) { return; } @@ -551,11 +565,14 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void dockTopTask(int topTaskId, int dragMode, int stackCreateMode, Rect initialBounds) { SystemServicesProxy ssp = Recents.getSystemServices(); + + // Make sure we inform DividerView before we actually start the activity so we can change + // the resize mode already. + EventBus.getDefault().send(new DockingTopTaskEvent(dragMode)); ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds); showRecents(false /* triggeredFromAltTab */, dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS, false /* animate */, true /* reloadTasks*/); - EventBus.getDefault().send(new DockingTopTaskEvent(dragMode)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java new file mode 100644 index 000000000000..11604b51b4a5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * This event is sent to request that the next task is launched after a double-tap on the Recents + * button. + */ +public class LaunchNextTaskRequestEvent extends EventBus.Event { + // Simple event +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java new file mode 100644 index 000000000000..d5083a8b017f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Fires when the user invoked the gesture to undock the task in the docked stack. + */ +public class UndockingTaskEvent extends EventBus.Event { + + public UndockingTaskEvent() { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java index ee3eb02c8a38..5eeda72637ea 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java @@ -67,9 +67,30 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd public static class ViewHolder extends RecyclerView.ViewHolder implements Task.TaskCallbacks { public final View content; - public ViewHolder(View v) { - super(v); - content = v; + private Task mTask; + + public ViewHolder(View content) { + super(content); + this.content = content; + } + + /** + * Binds this view holder to the given task. + */ + public void bindToTask(Task newTask) { + unbindFromTask(); + mTask = newTask; + mTask.addCallback(this); + } + + /** + * Unbinds this view holder from the + */ + public void unbindFromTask() { + if (mTask != null) { + mTask.removeCallback(this); + mTask = null; + } } @Override @@ -267,12 +288,13 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd } case TASK_ROW_VIEW_TYPE: { TaskRow taskRow = (TaskRow) row; - taskRow.task.addCallback(holder); TextView tv = (TextView) holder.content.findViewById(R.id.description); tv.setText(taskRow.task.title); ImageView iv = (ImageView) holder.content.findViewById(R.id.icon); iv.setAlpha(0f); holder.content.setOnClickListener(taskRow); + + holder.bindToTask(taskRow.task); loader.loadTaskData(taskRow.task, false /* fetchAndInvalidateThumbnails */); break; } @@ -289,7 +311,7 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd if (viewType == TASK_ROW_VIEW_TYPE) { TaskRow taskRow = (TaskRow) row; loader.unloadTaskData(taskRow.task); - taskRow.task.removeCallback(holder); + holder.unbindFromTask(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index e8fa39830f8d..1cd0850ef7fa 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -83,13 +83,11 @@ public class SystemBarScrimViews { * going home). */ public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { - int taskViewExitToAppDuration = mContext.getResources().getInteger( - R.integer.recents_task_exit_to_app_duration); if (mHasNavBarScrim && mShouldAnimateNavBarScrim) { mNavBarScrimView.animate() .translationY(mNavBarScrimView.getMeasuredHeight()) .setStartDelay(0) - .setDuration(taskViewExitToAppDuration) + .setDuration(TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java index 0eae183e58d6..7eaa1930f6a6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java @@ -108,7 +108,7 @@ public class TaskStackAnimationHelper { return; } - int offscreenY = stackLayout.mStackRect.bottom; + int offscreenYOffset = stackLayout.mStackRect.height(); int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize( R.dimen.recents_task_view_affiliate_group_enter_offset); @@ -145,7 +145,7 @@ public class TaskStackAnimationHelper { } else if (launchState.launchedFromHome) { // Move the task view off screen (below) so we can animate it in RectF bounds = new RectF(mTmpTransform.rect); - bounds.offsetTo(bounds.left, offscreenY); + bounds.offset(0, offscreenYOffset); tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom); } @@ -247,7 +247,7 @@ public class TaskStackAnimationHelper { return; } - int offscreenY = stackLayout.mStackRect.bottom; + int offscreenYOffset = stackLayout.mStackRect.height(); // Create the animations for each of the tasks List<TaskView> taskViews = mStackView.getTaskViews(); @@ -277,7 +277,7 @@ public class TaskStackAnimationHelper { stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform, null); - mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY); + mTmpTransform.rect.offset(0, offscreenYOffset); mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 46fdb2a9b860..bd37c3bfd761 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -383,6 +383,7 @@ public class TaskStackLayoutAlgorithm { */ void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet) { SystemServicesProxy ssp = Recents.getSystemServices(); + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); // Clear the progress map mTaskIndexMap.clear(); @@ -449,7 +450,6 @@ public class TaskStackLayoutAlgorithm { if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { mInitialScrollP = mMinScrollP; } else if (getDefaultFocusState() > 0f) { - RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); if (launchState.launchedFromHome) { mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, launchTaskIndex)); } else { @@ -568,7 +568,7 @@ public class TaskStackLayoutAlgorithm { boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task); if (isFrontMostTaskInGroup) { getStackTransform(taskProgress, mInitialScrollP, tmpTransform, null, - false /* ignoreSingleTaskCase */); + false /* ignoreSingleTaskCase */, false /* forceUpdate */); float screenY = tmpTransform.rect.top; boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight; if (hasVisibleThumbnail) { @@ -601,6 +601,12 @@ public class TaskStackLayoutAlgorithm { */ public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform) { + return getStackTransform(task, stackScroll, transformOut, frontTransform, + false /* forceUpdate */); + } + + public TaskViewTransform getStackTransform(Task task, float stackScroll, + TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) { if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) { mFreeformLayoutAlgorithm.getTransform(task, transformOut, this); return transformOut; @@ -610,8 +616,9 @@ public class TaskStackLayoutAlgorithm { transformOut.reset(); return transformOut; } - return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut, - frontTransform, false /* ignoreSingleTaskCase */); + getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut, + frontTransform, false /* ignoreSingleTaskCase */, forceUpdate); + return transformOut; } } @@ -635,9 +642,9 @@ public class TaskStackLayoutAlgorithm { * internally to ensure that we can calculate the transform for any * position in the stack. */ - public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, + public void getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform, - boolean ignoreSingleTaskCase) { + boolean ignoreSingleTaskCase, boolean forceUpdate) { SystemServicesProxy ssp = Recents.getSystemServices(); // Compute the focused and unfocused offset @@ -658,9 +665,9 @@ public class TaskStackLayoutAlgorithm { } // Skip if the task is not visible - if (!unfocusedVisible && !focusedVisible) { + if (!forceUpdate && !unfocusedVisible && !focusedVisible) { transformOut.reset(); - return transformOut; + return; } int x = (mStackRect.width() - mTaskRect.width()) / 2; @@ -700,7 +707,6 @@ public class TaskStackLayoutAlgorithm { transformOut.visible = (transformOut.rect.top < mStackRect.bottom) && (frontTransform == null || transformOut.rect.top != frontTransform.rect.top); transformOut.p = relP; - return transformOut; } /** @@ -797,8 +803,10 @@ public class TaskStackLayoutAlgorithm { mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin); float max = mUnfocusedRange.relativeMax + mFocusState * (mFocusedRange.relativeMax - mUnfocusedRange.relativeMax); - getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */); - getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */); + getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */, + true /* forceUpdate */); + getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */, + true /* forceUpdate */); mBackOfStackTransform.visible = true; mFrontOfStackTransform.visible = true; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 1c97b5a22ec7..bb74de493f58 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -58,6 +58,7 @@ import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationC import com.android.systemui.recents.events.activity.HideHistoryButtonEvent; import com.android.systemui.recents.events.activity.HideHistoryEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; +import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent; import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent; import com.android.systemui.recents.events.activity.PackagesChangedEvent; @@ -654,7 +655,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal transform.fillIn(tv); } else { mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), - transform, null); + transform, null, true /* forceUpdate */); } transform.visible = true; } @@ -1544,6 +1545,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mUIDozeTrigger.stopDozing(); } + public final void onBusEvent(LaunchNextTaskRequestEvent event) { + int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget()); + if (launchTaskIndex != -1) { + launchTaskIndex = Math.max(0, launchTaskIndex - 1); + } else { + launchTaskIndex = mStack.getTaskCount() - 1; + } + if (launchTaskIndex != -1) { + // Stop all animations + mUIDozeTrigger.stopDozing(); + cancelAllTaskViewAnimations(); + + Task launchTask = mStack.getStackTasks().get(launchTaskIndex); + EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask), + launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */)); + } + } + public final void onBusEvent(LaunchTaskStartedEvent event) { mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested, event.getAnimationTrigger()); @@ -1762,21 +1781,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) { - RecentsDebugFlags debugFlags = Recents.getDebugFlags(); - RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); - if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() && - RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) { - if (mFocusedTask != null) { - int timerIndicatorDuration = getResources().getInteger( - R.integer.recents_auto_advance_duration); - int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask); - setFocusedTask(focusedTaskIndex, false /* scrollToTask */, - false /* requestViewFocus */, timerIndicatorDuration); - } - } - } - public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) { List<TaskView> taskViews = getTaskViews(); int taskViewCount = taskViews.size(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index b8b506800488..d6680fdf23e2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -29,6 +29,7 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewParent; +import android.view.animation.Animation; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -497,6 +498,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // onBeginDrag(). mSv.removeIgnoreTask(tv.getTask()); mSv.updateLayoutAlgorithm(false /* boundScroll */); + mSv.relayoutTaskViews(AnimationProps.IMMEDIATE); mSwipeHelperAnimations.remove(v); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 703005f00a49..439d96f7d27b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -242,7 +242,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, - AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) { + AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) { RecentsConfiguration config = Recents.getConfiguration(); cancelTransformAnimation(); @@ -261,14 +261,16 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks updateCallback.onAnimationUpdate(null); } } else { + // Both the progress and the update are a function of the bounds movement of the task if (Float.compare(getTaskProgress(), toTransform.p) != 0) { - mTmpAnimators.add(ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(), - toTransform.p)); + ObjectAnimator anim = ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(), + toTransform.p); + mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim)); } if (updateCallback != null) { ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1); updateCallbackAnim.addUpdateListener(updateCallback); - mTmpAnimators.add(updateCallbackAnim); + mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim)); } // Create the animator diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java index 5ef56f3312dd..12e271397fba 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java @@ -26,10 +26,9 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Property; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.ImageButton; +import com.android.systemui.Interpolators; import com.android.systemui.R; /** @@ -71,7 +70,6 @@ public class DividerHandleView extends ImageButton { private final int mWidth; private final int mHeight; private final int mCircleDiameter; - private final Interpolator mFastOutSlowInInterpolator; private int mCurrentWidth; private int mCurrentHeight; private AnimatorSet mAnimator; @@ -85,8 +83,6 @@ public class DividerHandleView extends ImageButton { mCurrentWidth = mWidth; mCurrentHeight = mHeight; mCircleDiameter = (mWidth + mHeight) / 3; - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_slow_in); } public void setTouching(boolean touching, boolean animate) { @@ -120,8 +116,8 @@ public class DividerHandleView extends ImageButton { ? DividerView.TOUCH_ANIMATION_DURATION : DividerView.TOUCH_RELEASE_ANIMATION_DURATION); mAnimator.setInterpolator(touching - ? DividerView.TOUCH_RESPONSE_INTERPOLATOR - : mFastOutSlowInInterpolator); + ? Interpolators.TOUCH_RESPONSE + : Interpolators.FAST_OUT_SLOW_IN); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 1e11fa81f3bc..83c22b110433 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -48,10 +48,12 @@ import android.widget.FrameLayout; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; +import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DockingTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; +import com.android.systemui.recents.events.activity.UndockingTaskEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; @@ -67,8 +69,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, static final long TOUCH_ANIMATION_DURATION = 150; static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; - static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); private static final String TAG = "DividerView"; @@ -116,7 +116,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private final Rect mOtherInsetRect = new Rect(); private final Rect mLastResizeRect = new Rect(); private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance(); - private Interpolator mFastOutSlowInInterpolator; private DividerWindowManager mWindowManager; private VelocityTracker mVelocityTracker; private FlingAnimationUtils mFlingAnimationUtils; @@ -158,8 +157,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, mTouchElevation = getResources().getDimensionPixelSize( R.dimen.docked_stack_divider_lift_elevation); mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_slow_in); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f); updateDisplayInfo(); @@ -192,7 +189,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, insets.getStableInsetRight(), insets.getStableInsetBottom()); if (mSnapAlgorithm != null) { mSnapAlgorithm = null; - getSnapAlgorithm(); + initializeSnapAlgorithm(); } } return super.onApplyWindowInsets(insets); @@ -211,17 +208,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, mHandle.setTouching(true, animate); } mDockSide = mWindowManagerProxy.getDockSide(); - getSnapAlgorithm(); - if (mDockSide != WindowManager.DOCKED_INVALID) { - mWindowManagerProxy.setResizing(true); - mWindowManager.setSlippery(false); - if (touching) { - liftBackground(); - } - return true; - } else { - return false; + initializeSnapAlgorithm(); + mWindowManagerProxy.setResizing(true); + mWindowManager.setSlippery(false); + if (touching) { + liftBackground(); } + return mDockSide != WindowManager.DOCKED_INVALID; } public void stopDragging(int position, float velocity, boolean avoidDismissStart) { @@ -233,17 +226,36 @@ public class DividerView extends FrameLayout implements OnTouchListener, public void stopDragging(int position, SnapTarget target, long duration, Interpolator interpolator) { + stopDragging(position, target, duration, 0 /* startDelay*/, interpolator); + } + + public void stopDragging(int position, SnapTarget target, long duration, long startDelay, + Interpolator interpolator) { mHandle.setTouching(false, true /* animate */); - flingTo(position, target, duration, interpolator); + flingTo(position, target, duration, startDelay, interpolator); mWindowManager.setSlippery(true); releaseBackground(); } - public DividerSnapAlgorithm getSnapAlgorithm() { + private void stopDragging() { + mHandle.setTouching(false, true /* animate */); + mWindowManager.setSlippery(true); + releaseBackground(); + } + + private void updateDockSide() { + mDockSide = mWindowManagerProxy.getDockSide(); + } + + private void initializeSnapAlgorithm() { if (mSnapAlgorithm == null) { mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); } + } + + public DividerSnapAlgorithm getSnapAlgorithm() { + initializeSnapAlgorithm(); return mSnapAlgorithm; } @@ -267,6 +279,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, mStartX = (int) event.getX(); mStartY = (int) event.getY(); boolean result = startDragging(true /* animate */, true /* touching */); + if (!result) { + + // Weren't able to start dragging successfully, so cancel it again. + stopDragging(); + } mStartPosition = getCurrentPosition(); mMoving = false; return result; @@ -320,10 +337,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, anim.start(); } - private void flingTo(int position, SnapTarget target, long duration, + private void flingTo(int position, SnapTarget target, long duration, long startDelay, Interpolator interpolator) { ValueAnimator anim = getFlingAnimator(position, target); anim.setDuration(duration); + anim.setStartDelay(startDelay); anim.setInterpolator(interpolator); anim.start(); } @@ -377,7 +395,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mBackground.animate().scaleX(1.4f); } mBackground.animate() - .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) + .setInterpolator(Interpolators.TOUCH_RESPONSE) .setDuration(TOUCH_ANIMATION_DURATION) .translationZ(mTouchElevation) .start(); @@ -385,7 +403,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, // Lift handle as well so it doesn't get behind the background, even though it doesn't // cast shadow. mHandle.animate() - .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) + .setInterpolator(Interpolators.TOUCH_RESPONSE) .setDuration(TOUCH_ANIMATION_DURATION) .translationZ(mTouchElevation) .start(); @@ -393,14 +411,14 @@ public class DividerView extends FrameLayout implements OnTouchListener, private void releaseBackground() { mBackground.animate() - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) .translationZ(0) .scaleX(1f) .scaleY(1f) .start(); mHandle.animate() - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) .translationZ(0) .start(); @@ -421,6 +439,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mDisplayWidth = info.logicalWidth; mDisplayHeight = info.logicalHeight; mSnapAlgorithm = null; + initializeSnapAlgorithm(); } private int calculatePosition(int touchX, int touchY) { @@ -725,13 +744,29 @@ public class DividerView extends FrameLayout implements OnTouchListener, public final void onBusEvent(RecentsDrawnEvent drawnEvent) { if (mAnimateAfterRecentsDrawn) { mAnimateAfterRecentsDrawn = false; + updateDockSide(); stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250, - TOUCH_RESPONSE_INTERPOLATOR); + Interpolators.TOUCH_RESPONSE); } if (mGrowAfterRecentsDrawn) { mGrowAfterRecentsDrawn = false; + updateDockSide(); stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250, - TOUCH_RESPONSE_INTERPOLATOR); + Interpolators.TOUCH_RESPONSE); + } + } + + public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) { + int dockSide = mWindowManagerProxy.getDockSide(); + if (dockSide != WindowManager.DOCKED_INVALID) { + startDragging(false /* animate */, false /* touching */); + SnapTarget target = dockSideTopLeft(dockSide) + ? mSnapAlgorithm.getDismissEndTarget() + : mSnapAlgorithm.getDismissStartTarget(); + + // Don't start immediately - give a little bit time to settle the drag resize change. + stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */, + Interpolators.TOUCH_RESPONSE); } } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 24ab5063d94e..15bcaf834f72 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -97,7 +97,7 @@ public class WindowManagerProxy { @Override public void run() { try { - ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, false, + ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, true, false); } catch (RemoteException e) { Log.w(TAG, "Failed to resize stack: " + e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 963920c3b91e..0b7bfa8871ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -23,17 +23,15 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Handler; import android.os.Looper; -import android.util.DisplayMetrics; +import android.view.ContextThemeWrapper; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import com.android.systemui.R; @@ -65,7 +63,7 @@ public class KeyboardShortcuts { private Dialog mKeyboardShortcutsDialog; public KeyboardShortcuts(Context context) { - this.mContext = context; + this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light); } public void toggleKeyboardShortcuts() { @@ -108,40 +106,28 @@ public class KeyboardShortcuts { mHandler.post(new Runnable() { @Override public void run() { - // TODO: break all this code out into a handleShowKeyboard... - // Might add more things posted; should consider adding a custom handler so - // you can send the keyboardShortcutsGroups as part of the message. - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext); - LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( - LAYOUT_INFLATER_SERVICE); - final View keyboardShortcutsView = inflater.inflate( - R.layout.keyboard_shortcuts_view, null); - DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); - ScrollView scrollView = (ScrollView) keyboardShortcutsView.findViewById( - R.id.keyboard_shortcuts_scroll_view); - // TODO: find a better way to set the height. - scrollView.setLayoutParams(new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, - (int) (dm.heightPixels * dm.density))); - - populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById( - R.id.keyboard_shortcuts_container), keyboardShortcutGroups); - dialogBuilder.setView(keyboardShortcutsView); - dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener); - mKeyboardShortcutsDialog = dialogBuilder.create(); - mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); - - // Setup window. - Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); - keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); - keyboardShortcutsWindow.setBackgroundDrawable( - mContext.getDrawable(R.color.ksh_dialog_background_color)); - keyboardShortcutsWindow.setGravity(TOP); - mKeyboardShortcutsDialog.show(); + handleShowKeyboardShortcuts(keyboardShortcutGroups); } }); } + private void handleShowKeyboardShortcuts(List<KeyboardShortcutGroup> keyboardShortcutGroups) { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext); + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( + LAYOUT_INFLATER_SERVICE); + final View keyboardShortcutsView = inflater.inflate( + R.layout.keyboard_shortcuts_view, null); + populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById( + R.id.keyboard_shortcuts_container), keyboardShortcutGroups); + dialogBuilder.setView(keyboardShortcutsView); + dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener); + mKeyboardShortcutsDialog = dialogBuilder.create(); + mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); + Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); + keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); + mKeyboardShortcutsDialog.show(); + } + private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout, List<KeyboardShortcutGroup> keyboardShortcutGroups) { LayoutInflater inflater = LayoutInflater.from(mContext); @@ -156,36 +142,37 @@ public class KeyboardShortcuts { : mContext.getColor(R.color.ksh_application_group_color)); keyboardShortcutsLayout.addView(categoryTitle); - LinearLayout shortcutWrapper = (LinearLayout) inflater.inflate( - R.layout.keyboard_shortcuts_wrapper, null); + LinearLayout shortcutContainer = (LinearLayout) inflater.inflate( + R.layout.keyboard_shortcuts_container, keyboardShortcutsLayout, false); final int itemsSize = group.getItems().size(); for (int j = 0; j < itemsSize; j++) { KeyboardShortcutInfo info = group.getItems().get(j); - View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item, null); + View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item, + shortcutContainer, false); TextView textView = (TextView) shortcutView .findViewById(R.id.keyboard_shortcuts_keyword); textView.setText(info.getLabel()); + LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView + .findViewById(R.id.keyboard_shortcuts_item_container); List<String> shortcutKeys = getHumanReadableShortcutKeys(info); final int shortcutKeysSize = shortcutKeys.size(); for (int k = 0; k < shortcutKeysSize; k++) { String shortcutKey = shortcutKeys.get(k); TextView shortcutKeyView = (TextView) inflater.inflate( - R.layout.keyboard_shortcuts_key_view, null); + R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer, false); shortcutKeyView.setText(shortcutKey); - LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView - .findViewById(R.id.keyboard_shortcuts_item_container); shortcutItemsContainer.addView(shortcutKeyView); } - shortcutWrapper.addView(shortcutView); + shortcutContainer.addView(shortcutView); + } + keyboardShortcutsLayout.addView(shortcutContainer); + if (i < keyboardShortcutGroupsSize - 1) { + View separator = inflater.inflate( + R.layout.keyboard_shortcuts_category_separator, keyboardShortcutsLayout, + false); + keyboardShortcutsLayout.addView(separator); } - - // TODO: merge container and wrapper into one xml file - wrapper is always a child of - // container. - LinearLayout shortcutsContainer = (LinearLayout) inflater.inflate( - R.layout.keyboard_shortcuts_container, null); - shortcutsContainer.addView(shortcutWrapper); - keyboardShortcutsLayout.addView(shortcutsContainer); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index a2586f1984f2..bb03454de0f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -304,8 +304,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL public void onTuningChanged(String key, String newValue) { switch (key) { case KEY_DOCK_WINDOW_GESTURE: - mDockWindowEnabled = (newValue == null) || - (Integer.parseInt(newValue) != 0); + mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0); break; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index 935930195ba1..d625fc2bc738 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -27,7 +27,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Space; - import com.android.systemui.R; import com.android.systemui.statusbar.policy.KeyButtonView; import com.android.systemui.tuner.TunerService; @@ -58,8 +57,9 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi public static final String KEY_IMAGE_DELIM = ":"; public static final String KEY_CODE_END = ")"; - protected final LayoutInflater mLayoutInflater; - protected final LayoutInflater mLandscapeInflater; + protected LayoutInflater mLayoutInflater; + protected LayoutInflater mLandscapeInflater; + private int mDensity; protected FrameLayout mRot0; protected FrameLayout mRot90; @@ -69,11 +69,27 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi public NavigationBarInflaterView(Context context, AttributeSet attrs) { super(context, attrs); - mLayoutInflater = LayoutInflater.from(context); + mDensity = context.getResources().getConfiguration().densityDpi; + createInflaters(); + } + + private void createInflaters() { + mLayoutInflater = LayoutInflater.from(mContext); Configuration landscape = new Configuration(); - landscape.setTo(context.getResources().getConfiguration()); + landscape.setTo(mContext.getResources().getConfiguration()); landscape.orientation = Configuration.ORIENTATION_LANDSCAPE; - mLandscapeInflater = LayoutInflater.from(context.createConfigurationContext(landscape)); + mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape)); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mDensity != newConfig.densityDpi) { + mDensity = newConfig.densityDpi; + createInflaters(); + clearViews(); + inflateLayout(mCurrentLayout); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 575eda7cf873..f822bd535851 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -39,8 +39,6 @@ import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.TextView; @@ -212,10 +210,6 @@ public class NotificationPanelView extends PanelView implements } }; - /** Interpolator to be used for animations that respond directly to a touch */ - private final Interpolator mTouchResponseInterpolator = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); - public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(!DEBUG); @@ -1447,7 +1441,7 @@ public class NotificationPanelView extends PanelView implements mScrollView.setBlockFlinging(true); ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); if (isClick) { - animator.setInterpolator(mTouchResponseInterpolator); + animator.setInterpolator(Interpolators.TOUCH_RESPONSE); animator.setDuration(368); } else { mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 07dc4fd5fb7c..4dee51df131c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -118,7 +118,10 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.UndockingTaskEvent; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.stackdivider.WindowManagerProxy; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.BaseStatusBar; @@ -1112,26 +1115,25 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public boolean onLongClick(View v) { if (mRecents != null) { - Point realSize = new Point(); - mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) - .getRealSize(realSize); - Rect initialBounds; - - // Hack level over 9000: Make it one pixel smaller so activity manager doesn't - // dismiss it immediately again. Remove once b/26777526 is fixed. - if (mContext.getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE) { - initialBounds = new Rect(0, 0, realSize.x - 1, realSize.y); + int dockSide = WindowManagerProxy.getInstance().getDockSide(); + if (dockSide == WindowManager.DOCKED_INVALID) { + Point realSize = new Point(); + mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) + .getRealSize(realSize); + Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y); + boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, + ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, + initialBounds); + if (docked) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS); + return true; + } } else { - initialBounds = new Rect(0, 0, realSize.x, realSize.y - 1); - } - boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, - ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, - initialBounds); - if (docked) { - MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS); + EventBus.getDefault().send(new UndockingTaskEvent()); + MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS); return true; } + } return false; } 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 9996b7567893..45aae2dd61f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -30,12 +30,12 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.ArraySet; import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.BatteryMeterView; import com.android.systemui.FontSizeUtils; @@ -226,12 +226,25 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl public void setExternalIcon(String slot) { int viewIndex = getViewIndex(getSlotIndex(slot)); + int height = mContext.getResources().getDimensionPixelSize( + R.dimen.status_bar_icon_drawing_size); ImageView imageView = (ImageView) mStatusIcons.getChildAt(viewIndex); imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); imageView.setAdjustViewBounds(true); + setHeightAndCenter(imageView, height); imageView = (ImageView) mStatusIconsKeyguard.getChildAt(viewIndex); imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); imageView.setAdjustViewBounds(true); + setHeightAndCenter(imageView, height); + } + + private void setHeightAndCenter(ImageView imageView, int height) { + ViewGroup.LayoutParams params = imageView.getLayoutParams(); + params.height = height; + if (params instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL; + } + imageView.setLayoutParams(params); } public void setIcon(String slot, StatusBarIcon icon) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java index b0369368b4b2..500d60359145 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java @@ -20,7 +20,6 @@ public interface HotspotController { void addCallback(Callback callback); void removeCallback(Callback callback); boolean isHotspotEnabled(); - boolean isHotspotSupported(); void setHotspotEnabled(boolean enabled); boolean isTetheringAllowed(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 61d26c72a898..07b74094ffb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -27,8 +27,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; -import com.android.settingslib.TetherUtil; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -98,11 +96,6 @@ public class HotspotControllerImpl implements HotspotController { } @Override - public boolean isHotspotSupported() { - return TetherUtil.isTetheringSupported(mContext); - } - - @Override public boolean isTetheringAllowed() { return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(mCurrentUser)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index bc8c82572c9d..784f610e40a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -80,10 +80,6 @@ public class TvStatusBar extends BaseStatusBar { boolean showImeSwitcher) { } - @Override - public void toggleRecentApps() { - } - @Override // CommandQueue public void setWindowState(int window, int state) { } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 0e67a24748b8..e1dd87f55ed4 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -348,5 +348,9 @@ message MetricsEvent { // OS: 6.1 // GMS: 7.8.99 USER_CREDENTIALS = 285; + + // Logged when the user undocks a previously docked window by long pressing recents while in + // docked mode. + ACTION_WINDOW_UNDOCK_LONGPRESS = 286; } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 3fd8b40dbc14..430092071d90 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2670,18 +2670,21 @@ public class ConnectivityService extends IConnectivityManager.Stub // if ro.tether.denied = true we default to no tethering // gservices could set the secure setting to 1 though to enable it on a build where it // had previously been turned off. + @Override public boolean isTetheringSupported() { enforceTetherAccessPermission(); int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1); boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.TETHER_SUPPORTED, defaultVal) != 0) && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 || + return tetherEnabledInSettings && mUserManager.isAdminUser() && + ((mTethering.getTetherableUsbRegexs().length != 0 || mTethering.getTetherableWifiRegexs().length != 0 || mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceTypes().length != 0); } + @Override public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { ConnectivityManager.enforceTetherChangePermission(mContext); @@ -2692,6 +2695,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mTethering.startTethering(type, receiver, showProvisioningUi); } + @Override public void stopTethering(int type) { ConnectivityManager.enforceTetherChangePermission(mContext); mTethering.stopTethering(type); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 9bd79c9cf6c9..423ef84dd625 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1649,6 +1649,7 @@ public class DeviceIdleController extends SystemService // Whoops, there is an upcoming alarm. We don't actually want to go idle. if (mState != STATE_ACTIVE) { becomeActiveLocked("alarm", Process.myUid()); + becomeInactiveIfAppropriateLocked(); } return; } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 3ce44526d17b..5120e1b08f13 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -2800,14 +2800,13 @@ class MountService extends IMountService.Stub } @Override - public void prepareUserStorage( - String volumeUuid, int userId, int serialNumber, boolean ephemeral) { + public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); waitForReady(); try { mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid), - userId, serialNumber, ephemeral ? 1 : 0); + userId, serialNumber, flags); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 4f0d4d951e1c..bef6f0aabf5d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -62,7 +62,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_LRU = DEBUG_ALL || false; static final boolean DEBUG_MU = DEBUG_ALL || false; static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false; - static final boolean DEBUG_PAUSE = DEBUG_ALL || false; + static final boolean DEBUG_PAUSE = DEBUG_ALL || true; static final boolean DEBUG_POWER = DEBUG_ALL || false; static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false; static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false; @@ -77,7 +77,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_SERVICE = DEBUG_ALL || false; static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false; static final boolean DEBUG_STACK = DEBUG_ALL || false; - static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false; + static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || true; static final boolean DEBUG_SWITCH = DEBUG_ALL || false; static final boolean DEBUG_TASKS = DEBUG_ALL || false; static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false; @@ -85,7 +85,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false; static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false; static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false; - static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false; + static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true; static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false; static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 072249f5928b..c021f4cfccaa 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -252,10 +252,12 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; @@ -600,6 +602,8 @@ public final class ActivityManagerService extends ActivityManagerNative final AppErrors mAppErrors; + boolean mDoingSetFocusedActivity; + public boolean canShowErrorDialogs() { return mShowDialogs && !mSleeping && !mShuttingDown; } @@ -2730,6 +2734,12 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r); + + final boolean wasDoingSetFocusedActivity = mDoingSetFocusedActivity; + if (wasDoingSetFocusedActivity) Slog.w(TAG, + "setFocusedActivityLocked: called recursively, r=" + r + ", reason=" + reason); + mDoingSetFocusedActivity = true; + final ActivityRecord last = mFocusedActivity; mFocusedActivity = r; if (r.task.isApplicationTask()) { @@ -2781,6 +2791,12 @@ public final class ActivityManagerService extends ActivityManagerNative mLastFocusedUserId = mFocusedActivity.userId; } + // Log a warning if the focused app is changed during the process. This could + // indicate a problem of the focus setting logic! + if (mFocusedActivity != r) Slog.w(TAG, + "setFocusedActivityLocked: r=" + r + " but focused to " + mFocusedActivity); + mDoingSetFocusedActivity = wasDoingSetFocusedActivity; + EventLogTags.writeAmFocusedActivity( mFocusedActivity == null ? -1 : mFocusedActivity.userId, mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName, @@ -5337,7 +5353,8 @@ public final class ActivityManagerService extends ActivityManagerNative // We don't kill persistent processes. continue; } - if (targetSdkVersion > 0 && app.info.targetSdkVersion < targetSdkVersion) { + if (targetSdkVersion > 0 + && app.info.targetSdkVersion >= targetSdkVersion) { continue; } if (app.removed) { @@ -8753,6 +8770,13 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } + if (!tr.mUserSetupComplete) { + // Don't include task launched while user is not done setting-up. + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, + "Skipping, user setup not complete: " + tr); + continue; + } + ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr); if (!detailed) { rti.baseIntent.replaceExtras((Bundle)null); @@ -12408,9 +12432,8 @@ public final class ActivityManagerService extends ActivityManagerNative mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); - // Make sure we have the current profile info, since it is needed for - // security checks. - mUserController.updateCurrentProfileIdsLocked(); + // Make sure we have the current profile info, since it is needed for security checks. + mUserController.onSystemReady(); mRecentTasks.onSystemReady(); // Check to see if there are any update receivers to run. @@ -17631,6 +17654,22 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); final ActivityStack stack = mStackSupervisor.getStack(fromStackId); if (stack != null) { + if (fromStackId == DOCKED_STACK_ID) { + + // We are moving all tasks from the docked stack to the fullscreen stack, which + // is dismissing the docked stack, so resize all other stacks to fullscreen here + // already so we don't end up with resize trashing. + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (StackId.isResizeableByDockedStack(i)) { + ActivityStack otherStack = mStackSupervisor.getStack(i); + if (otherStack != null) { + mStackSupervisor.resizeStackLocked(i, + null, null, null, PRESERVE_WINDOWS, + true /* allowResizeInDockedMode */); + } + } + } + } final ArrayList<TaskRecord> tasks = stack.getAllTasks(); final int size = tasks.size(); if (onTop) { @@ -17769,7 +17808,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (values.getLocales().size() == 1) { // This is an optimization to avoid the JNI call when the result of // getFirstMatch() does not depend on the supported locales. - locale = values.getLocales().getPrimary(); + locale = values.getLocales().get(0); } else { if (mSupportedSystemLocales == null) { mSupportedSystemLocales = diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 5a0c1c1bcd72..c352fc819884 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2168,7 +2168,9 @@ final class ActivityStack { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next); // This activity is now becoming visible. - mWindowManager.setAppVisibility(next.appToken, true); + if (!next.visible) { + mWindowManager.setAppVisibility(next.appToken, true); + } // schedule launch ticks to collect information about slow apps. next.startLaunchTickingLocked(); @@ -4304,6 +4306,12 @@ final class ActivityStack { oldTaskOverride = record.task.extractOverrideConfig(record.configuration); } + // Conversely, do the same when going the other direction. + if (Configuration.EMPTY.equals(taskConfig) + && !Configuration.EMPTY.equals(oldTaskOverride)) { + taskConfig = record.task.extractOverrideConfig(record.configuration); + } + // Determine what has changed. May be nothing, if this is a config // that has come back from the app after going idle. In that case // we just want to leave the official config object now in the diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 95bc95a8767a..0b2ff655597d 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -583,7 +583,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } final ActivityRecord r = topRunningActivityLocked(); - if (mService.mFocusedActivity != r) { + if (!mService.mDoingSetFocusedActivity && mService.mFocusedActivity != r) { // The focus activity should always be the top activity in the focused stack. // There will be chaos and anarchy if it isn't... mService.setFocusedActivityLocked(r, reason + " setFocusStack"); @@ -1889,12 +1889,6 @@ public final class ActivityStackSupervisor implements DisplayListener { private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) { - if (bounds != null && mWindowManager.isFullscreenBounds(stack.mStackId, bounds)) { - // The bounds passed in corresponds to the fullscreen bounds which we normally - // represent with null. Go ahead and set it to null so that all tasks configuration - // can have the right fullscreen state. - bounds = null; - } bounds = TaskRecord.validateBounds(bounds); mTmpBounds.clear(); @@ -1913,7 +1907,8 @@ public final class ActivityStackSupervisor implements DisplayListener { task.updateOverrideConfiguration(tempRect2); } else { task.updateOverrideConfiguration( - tempTaskBounds != null ? tempTaskBounds : bounds); + tempTaskBounds != null ? tempTaskBounds : bounds, + tempTaskInsetBounds != null ? tempTaskInsetBounds : bounds); } } @@ -2213,9 +2208,9 @@ public final class ActivityStackSupervisor implements DisplayListener { // Temporarily disable resizeablility of task we are moving. We don't want it to be resized // if a docked stack is created below which will lead to the stack we are moving from and // its resizeable tasks being resized. - task.mResizeMode = RESIZE_MODE_UNRESIZEABLE; + task.mTemporarilyUnresizable = true; final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop); - task.mResizeMode = resizeMode; + task.mTemporarilyUnresizable = false; mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop); stack.addTask(task, toTop, reason); @@ -2606,9 +2601,11 @@ public final class ActivityStackSupervisor implements DisplayListener { stack.setVisibleBehindActivity(visible ? r : null); if (!visible) { - // Make the activity immediately above r opaque. + // If there is a translucent home activity, we need to force it stop being translucent, + // because we can't depend on the application to necessarily perform that operation. + // Check out b/14469711 for details. final ActivityRecord next = stack.findNextTranslucentActivity(r); - if (next != null) { + if (next != null && next.isHomeActivity()) { mService.convertFromTranslucent(next.appToken); } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 97ef10b10ab2..28882def6b99 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -81,12 +81,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub Context mContext; PowerManagerInternal mPowerManagerInternal; - final int UPDATE_CPU = 0x01; - final int UPDATE_WIFI = 0x02; - final int UPDATE_RADIO = 0x04; - final int UPDATE_BT = 0x08; - final int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT; - class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync { public static final int MSG_SYNC_EXTERNAL_STATS = 1; public static final int MSG_WRITE_TO_DISK = 2; @@ -133,16 +127,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public void scheduleSync(String reason) { - synchronized (this) { - scheduleSyncLocked(reason, UPDATE_ALL); - } - } - - @Override - public void scheduleWifiSync(String reason) { + public void scheduleSync(String reason, int updateFlags) { synchronized (this) { - scheduleSyncLocked(reason, UPDATE_WIFI); + scheduleSyncLocked(reason, updateFlags); } } @@ -194,7 +181,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void shutdown() { Slog.w("BatteryStats", "Writing battery stats before shutdown..."); - updateExternalStats("shutdown", UPDATE_ALL); + updateExternalStats("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); synchronized (mStats) { mStats.shutdownLocked(); } @@ -294,7 +281,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub //Slog.i("foo", "SENDING BATTERY INFO:"); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); - updateExternalStats("get-stats", UPDATE_ALL); + updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); synchronized (mStats) { mStats.writeToParcel(out, 0); } @@ -309,7 +296,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub //Slog.i("foo", "SENDING BATTERY INFO:"); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); - updateExternalStats("get-stats", UPDATE_ALL); + updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); synchronized (mStats) { mStats.writeToParcel(out, 0); } @@ -672,7 +659,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub final String type = (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM) ? "active" : "inactive"; - mHandler.scheduleWifiSync("wifi-data: " + type); + mHandler.scheduleSync("wifi-data: " + type, + BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI); } mStats.noteWifiRadioPowerState(powerState, tsNanos); } @@ -860,13 +848,25 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBleScanStarted(WorkSource ws) { enforceCallingPermission(); - Slog.d(TAG, "BLE scan started for " + ws); + synchronized (mStats) { + mStats.noteBluetoothScanStartedFromSourceLocked(ws); + } } @Override public void noteBleScanStopped(WorkSource ws) { enforceCallingPermission(); - Slog.d(TAG, "BLE scan stopped for " + ws); + synchronized (mStats) { + mStats.noteBluetoothScanStoppedFromSourceLocked(ws); + } + } + + @Override + public void noteResetBleScan() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteResetBluetoothScanLocked(); + } } public boolean isOnBattery() { @@ -895,7 +895,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub // Sync external stats first as the battery has changed states. If we don't sync // immediately here, we may not collect the relevant data later. - updateExternalStats("battery-state", UPDATE_ALL); + updateExternalStats("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); synchronized (mStats) { mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt); } @@ -1082,9 +1082,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub pw.println("Battery stats reset."); noOutput = true; } - updateExternalStats("dump", UPDATE_ALL); + updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); } else if ("--write".equals(arg)) { - updateExternalStats("dump", UPDATE_ALL); + updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); synchronized (mStats) { mStats.writeSyncLocked(); pw.println("Battery stats written."); @@ -1148,7 +1148,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY; } // Fetch data from external sources and update the BatteryStatsImpl object with them. - updateExternalStats("dump", UPDATE_ALL); + updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); } finally { Binder.restoreCallingIdentity(ident); } @@ -1358,8 +1358,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub * * @param reason The reason why this collection was requested. Useful for debugging. * @param updateFlags Which external stats to update. Can be a combination of - * {@link #UPDATE_CPU}, {@link #UPDATE_RADIO}, {@link #UPDATE_WIFI}, - * and {@link #UPDATE_BT}. + * {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_CPU}, + * {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_RADIO}, + * {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI}, + * and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}. */ void updateExternalStats(final String reason, final int updateFlags) { synchronized (mExternalStatsLock) { @@ -1374,17 +1376,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub } WifiActivityEnergyInfo wifiEnergyInfo = null; - if ((updateFlags & UPDATE_WIFI) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { wifiEnergyInfo = pullWifiEnergyInfoLocked(); } ModemActivityInfo modemActivityInfo = null; - if ((updateFlags & UPDATE_RADIO) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { modemActivityInfo = pullModemActivityInfoLocked(); } BluetoothActivityEnergyInfo bluetoothEnergyInfo = null; - if ((updateFlags & UPDATE_BT) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) { // We only pull bluetooth stats when we have to, as we are not distributing its // use amongst apps and the sampling frequency does not matter. bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked(); @@ -1398,20 +1400,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0); } - if ((updateFlags & UPDATE_CPU) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU) != 0) { mStats.updateCpuTimeLocked(); mStats.updateKernelWakelocksLocked(); } - if ((updateFlags & UPDATE_RADIO) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { mStats.updateMobileRadioStateLocked(elapsedRealtime, modemActivityInfo); } - if ((updateFlags & UPDATE_WIFI) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { mStats.updateWifiStateLocked(wifiEnergyInfo); } - if ((updateFlags & UPDATE_BT) != 0) { + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) { mStats.updateBluetoothStateLocked(bluetoothEnergyInfo); } } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index a7d948c9a695..10f09775608b 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -16,37 +16,7 @@ package com.android.server.am; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; -import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; -import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityManagerService.LOCK_SCREEN_SHOWN; -import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; -import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; -import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE; - +import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.StackId; @@ -55,6 +25,7 @@ import android.app.ActivityManager.TaskThumbnail; import android.app.ActivityManager.TaskThumbnailInfo; import android.app.ActivityOptions; import android.app.AppGlobals; +import android.app.IActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -86,6 +57,37 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.HOME_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; +import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; +import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityManagerService.LOCK_SCREEN_SHOWN; +import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; +import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; +import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE; + final class TaskRecord { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM; private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; @@ -106,6 +108,7 @@ final class TaskRecord { private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents"; private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode"; private static final String ATTR_USERID = "user_id"; + private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete"; private static final String ATTR_EFFECTIVE_UID = "effective_uid"; private static final String ATTR_TASKTYPE = "task_type"; private static final String ATTR_FIRSTACTIVETIME = "first_active_time"; @@ -155,11 +158,15 @@ final class TaskRecord { String stringName; // caching of toString() result. int userId; // user for which this task was created + boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity + // was changed. int numFullscreen; // Number of fullscreen activities. int mResizeMode; // The resize mode of this task and its activities. // Based on the {@link ActivityInfo#resizeMode} of the root activity. + boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize + // changes on a temporary basis. int mLockTaskMode; // Which tasklock mode to launch this task in. One of // ActivityManager.LOCK_TASK_LAUNCH_MODE_* private boolean mPrivileged; // The root activity application of this task holds @@ -238,6 +245,9 @@ final class TaskRecord { // Bounds of the Task. null for fullscreen tasks. Rect mBounds = null; + private final Rect mTmpRect = new Rect(); + private final Rect mTmpRect2 = new Rect(); + // Last non-fullscreen bounds the task was launched in or resized to. // The information is persisted and used to determine the appropriate stack to launch the // task into on restore. @@ -311,7 +321,8 @@ final class TaskRecord { boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage, - int resizeMode, boolean privileged, boolean _realActivitySuspended) { + int resizeMode, boolean privileged, boolean _realActivitySuspended, + boolean userSetupComplete) { mService = service; mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; @@ -334,6 +345,7 @@ final class TaskRecord { taskType = _taskType; mTaskToReturnTo = HOME_ACTIVITY_TYPE; userId = _userId; + mUserSetupComplete = userSetupComplete; effectiveUid = _effectiveUid; firstActiveTime = _firstActiveTime; lastActiveTime = _lastActiveTime; @@ -434,6 +446,7 @@ final class TaskRecord { } userId = UserHandle.getUserId(info.applicationInfo.uid); + mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId); if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) { // If the activity itself has requested auto-remove, then just always do it. autoRemoveRecents = true; @@ -934,7 +947,7 @@ final class TaskRecord { boolean isResizeable() { return !isHomeTask() && (mService.mForceResizableActivities - || ActivityInfo.isResizeableMode(mResizeMode)); + || ActivityInfo.isResizeableMode(mResizeMode)) && !mTemporarilyUnresizable; } boolean inCropWindowsResizeMode() { @@ -1064,6 +1077,7 @@ final class TaskRecord { out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); out.attribute(null, ATTR_USERID, String.valueOf(userId)); + out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete)); out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid)); out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType)); out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime)); @@ -1133,6 +1147,7 @@ final class TaskRecord { boolean askedCompatMode = false; int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE; int userId = 0; + boolean userSetupComplete = true; int effectiveUid = -1; String lastDescription = null; long firstActiveTime = -1; @@ -1179,6 +1194,8 @@ final class TaskRecord { askedCompatMode = Boolean.valueOf(attrValue); } else if (ATTR_USERID.equals(attrName)) { userId = Integer.valueOf(attrValue); + } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) { + userSetupComplete = Boolean.valueOf(attrValue); } else if (ATTR_EFFECTIVE_UID.equals(attrName)) { effectiveUid = Integer.valueOf(attrValue); } else if (ATTR_TASKTYPE.equals(attrName)) { @@ -1278,7 +1295,7 @@ final class TaskRecord { activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged, - realActivitySuspended); + realActivitySuspended, userSetupComplete); task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { @@ -1291,9 +1308,22 @@ final class TaskRecord { /** * Update task's override configuration based on the bounds. + * @param bounds The bounds of the task. * @return Update configuration or null if there is no change. */ Configuration updateOverrideConfiguration(Rect bounds) { + return updateOverrideConfiguration(bounds, null /* insetBounds */); + } + + /** + * Update task's override configuration based on the bounds. + * @param bounds The bounds of the task. + * @param insetBounds The bounds used to calculate the system insets, which is used here to + * subtract the navigation bar/status bar size from the screen size reported + * to the application. See {@link IActivityManager#resizeDockedStack}. + * @return Update configuration or null if there is no change. + */ + Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) { if (Objects.equals(mBounds, bounds)) { return null; } @@ -1316,7 +1346,12 @@ final class TaskRecord { if (stack == null || StackId.persistTaskBounds(stack.mStackId)) { mLastNonFullscreenBounds = mBounds; } - mOverrideConfig = calculateOverrideConfig(mBounds); + + // Stable insets need to be subtracted because we also subtract it in the fullscreen + // configuration. + mTmpRect.set(bounds); + subtractStableInsets(mTmpRect, insetBounds != null ? insetBounds : mTmpRect); + mOverrideConfig = calculateOverrideConfig(mTmpRect); } if (mFullscreen != oldFullscreen) { @@ -1326,6 +1361,16 @@ final class TaskRecord { return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null; } + private void subtractStableInsets(Rect inOutBounds, Rect inInsetBounds) { + mTmpRect2.set(inInsetBounds); + mService.mWindowManager.subtractStableInsets(mTmpRect2); + int leftInset = mTmpRect2.left - inInsetBounds.left; + int topInset = mTmpRect2.top - inInsetBounds.top; + int rightInset = inInsetBounds.right - mTmpRect2.right; + int bottomInset = inInsetBounds.bottom - mTmpRect2.bottom; + inOutBounds.inset(leftInset, topInset, rightInset, bottomInset); + } + Configuration calculateOverrideConfig(Rect bounds) { final Configuration serviceConfig = mService.mConfiguration; final Configuration config = new Configuration(Configuration.EMPTY); @@ -1341,8 +1386,9 @@ final class TaskRecord { ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout); - config.screenLayout = Configuration.reduceScreenLayout( - sl, config.screenWidthDp, config.screenHeightDp); + int longSize = Math.max(config.screenWidthDp, config.screenHeightDp); + int shortSize = Math.min(config.screenWidthDp, config.screenHeightDp); + config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize); return config; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 551f332cce8f..2f63b2d3e9bf 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; import static android.content.Context.KEYGUARD_SERVICE; import static android.os.Process.SYSTEM_UID; +import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -45,11 +46,14 @@ import android.app.Dialog; import android.app.IStopUserCallback; import android.app.IUserSwitchObserver; import android.app.KeyguardManager; +import android.content.ContentResolver; import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; @@ -66,10 +70,12 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IMountService; import android.os.storage.StorageManager; +import android.provider.Settings; import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.internal.R; @@ -145,6 +151,34 @@ final class UserController { private final LockPatternUtils mLockPatternUtils; + // Set of users who have completed the set-up process. + private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray(); + private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver; + + private class UserSetupCompleteContentObserver extends ContentObserver { + private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE); + + public UserSetupCompleteContentObserver(Handler handler) { + super(handler); + } + + void register(ContentResolver resolver) { + resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL); + synchronized (mService) { + updateCurrentUserSetupCompleteLocked(); + } + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (mUserSetupComplete.equals(uri)) { + synchronized (mService) { + updateCurrentUserSetupCompleteLocked(); + } + } + } + } + UserController(ActivityManagerService service) { mService = service; mHandler = mService.mHandler; @@ -154,6 +188,7 @@ final class UserController { mUserLru.add(UserHandle.USER_SYSTEM); mLockPatternUtils = new LockPatternUtils(mService.mContext); updateStartedUserArrayLocked(); + mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler); } void finishUserSwitch(UserState uss) { @@ -424,6 +459,7 @@ final class UserController { mStartedUsers.remove(userId); mUserLru.remove(Integer.valueOf(userId)); updateStartedUserArrayLocked(); + mSetupCompletedUsers.delete(userId); mService.onUserStoppedLocked(userId); // Clean up all state and processes associated with the user. @@ -619,6 +655,7 @@ final class UserController { final Integer userIdInt = userId; mUserLru.remove(userIdInt); mUserLru.add(userIdInt); + updateCurrentUserSetupCompleteLocked(); if (foreground) { mCurrentUserId = userId; @@ -833,6 +870,17 @@ final class UserController { mUserSwitchObservers.finishBroadcast(); } + void updateCurrentUserSetupCompleteLocked() { + final ContentResolver cr = mService.mContext.getContentResolver(); + final boolean setupComplete = + Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0; + mSetupCompletedUsers.put(mCurrentUserId, setupComplete); + } + + boolean isUserSetupCompleteLocked(int userId) { + return mSetupCompletedUsers.get(userId); + } + private void stopBackgroundUsersIfEnforced(int oldUserId) { // Never stop system user if (oldUserId == UserHandle.USER_SYSTEM) { @@ -1141,12 +1189,17 @@ final class UserController { } } + void onSystemReady() { + updateCurrentProfileIdsLocked(); + mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver()); + } + /** * Refreshes the list of users related to the current user when either a * user switch happens or when a new related user is started in the * background. */ - void updateCurrentProfileIdsLocked() { + private void updateCurrentProfileIdsLocked() { final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId, false /* enabledOnly */); int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index a73a67a9ca7d..760b21819420 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -451,7 +451,7 @@ public class Tethering extends BaseNetworkObserver { ((BluetoothPan) proxy).setBluetoothTethering(enable); // TODO: Enabling bluetooth tethering can fail asynchronously here. // We should figure out a way to bubble up that failure instead of sending success. - int result = ((BluetoothPan) proxy).isTetheringOn() ? + int result = ((BluetoothPan) proxy).isTetheringOn() == enable ? ConnectivityManager.TETHER_ERROR_NO_ERROR : ConnectivityManager.TETHER_ERROR_MASTER_ERROR; sendTetherResult(receiver, result); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index be55799dd763..e749433b1e3f 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -349,9 +349,9 @@ public class JobStatus { pw.print(prefix); UserHandle.formatUid(pw, uId); pw.print(" tag="); pw.println(tag); pw.print(prefix); - pw.print("Source: uid="); UserHandle.formatUid(pw, sourceUid); - pw.print(" user="); pw.print(sourceUserId); - pw.print(" pkg="); pw.println(sourcePackageName); + pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); + pw.print(" user="); pw.print(getSourceUserId()); + pw.print(" pkg="); pw.println(getSourcePackageName()); pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix); pw.print(" Service: "); pw.println(job.getService().flattenToShortString()); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index f5da52ee733c..29d52c1739ba 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -250,6 +250,12 @@ abstract public class ManagedServices { rebindServices(); } + public void onUserUnlocked(int user) { + if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user); + rebuildRestoredPackages(); + rebindServices(); + } + public ManagedServiceInfo getServiceFromTokenLocked(IInterface service) { if (service == null) { return null; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 078094cfef7a..bcb2c598c82d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -816,6 +816,12 @@ public class NotificationManagerService extends SystemService { } else if (action.equals(Intent.ACTION_USER_REMOVED)) { final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); mZenModeHelper.onUserRemoved(user); + } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { + final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + mConditionProviders.onUserUnlocked(user); + mListeners.onUserUnlocked(user); + mAssistant.onUserUnlocked(user); + mZenModeHelper.onUserUnlocked(user); } } }; @@ -994,6 +1000,7 @@ public class NotificationManagerService extends SystemService { filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED); getContext().registerReceiver(mIntentReceiver, filter); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index b7abce2159d3..7518c6e316f9 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -16,7 +16,6 @@ package com.android.server.notification; -import static android.media.AudioAttributes.USAGE_ALARM; import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.media.AudioAttributes.USAGE_UNKNOWN; @@ -195,27 +194,35 @@ public class ZenModeHelper { } public void onUserSwitched(int user) { + loadConfigForUser(user, "onUserSwitched"); + } + + public void onUserRemoved(int user) { + if (user < UserHandle.USER_SYSTEM) return; + if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); + mConfigs.remove(user); + } + + public void onUserUnlocked(int user) { + loadConfigForUser(user, "onUserUnlocked"); + } + + private void loadConfigForUser(int user, String reason) { if (mUser == user || user < UserHandle.USER_SYSTEM) return; mUser = user; - if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user); + if (DEBUG) Log.d(TAG, reason + " u=" + user); ZenModeConfig config = mConfigs.get(user); if (config == null) { - if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user); + if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user); config = mDefaultConfig.copy(); config.user = user; } synchronized (mConfig) { - setConfigLocked(config, "onUserSwitched"); + setConfigLocked(config, reason); } cleanUpZenRules(); } - public void onUserRemoved(int user) { - if (user < UserHandle.USER_SYSTEM) return; - if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); - mConfigs.remove(user); - } - public int getZenModeListenerInterruptionFilter() { return NotificationManager.zenModeToInterruptionFilter(mZenMode); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index dac89ec759f7..c08f71314d1f 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -16,11 +16,11 @@ package com.android.server.pm; -import android.annotation.IntDef; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageStats; import android.os.Build; +import android.os.storage.StorageManager; import android.util.Slog; import com.android.internal.os.InstallerConnection; @@ -29,9 +29,6 @@ import com.android.server.SystemService; import dalvik.system.VMRuntime; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - public final class Installer extends SystemService { private static final String TAG = "Installer"; @@ -52,19 +49,9 @@ public final class Installer extends SystemService { /** This is an OTA update dexopt */ public static final int DEXOPT_OTA = 1 << 6; - /** @hide */ - @IntDef(flag = true, value = { - FLAG_DE_STORAGE, - FLAG_CE_STORAGE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface StorageFlags {} - - public static final int FLAG_DE_STORAGE = 1 << 0; - public static final int FLAG_CE_STORAGE = 1 << 1; - - public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2; - public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3; + // NOTE: keep in sync with installd + public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8; + public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; private final InstallerConnection mInstaller; diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 64af21313a1d..a3af561bf44f 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -176,8 +176,14 @@ class PackageDexOptimizer { dexoptNeeded = adjustDexoptNeeded(dexoptNeeded); if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { - // No dexopt needed and we don't use profiles. Nothing to do. - continue; + if (useProfiles) { + // Profiles may trigger re-compilation. The final decision is taken in + // installd. + dexoptNeeded = DexFile.DEX2OAT_NEEDED; + } else { + // No dexopt needed and we don't use profiles. Nothing to do. + continue; + } } final String dexoptType; String oatDir = null; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 553a9a26de16..504ce3174d20 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -235,7 +235,6 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; -import com.android.server.pm.Installer.StorageFlags; import com.android.server.pm.PermissionsState.PermissionState; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.pm.Settings.VersionInfo; @@ -2388,9 +2387,9 @@ public class PackageManagerService extends IPackageManager.Stub { // can't wait for user to start final int flags; if (StorageManager.isFileBasedEncryptionEnabled()) { - flags = Installer.FLAG_DE_STORAGE; + flags = StorageManager.FLAG_STORAGE_DE; } else { - flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE; + flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, flags); @@ -6860,7 +6859,7 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean removeDataDirsLI(String volumeUuid, String packageName) { // TODO: triage flags as part of 26466827 - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; boolean res = true; final int[] users = sUserManager.getUserIds(); @@ -6906,7 +6905,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) { // TODO: triage flags as part of 26466827 - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; final int[] users = sUserManager.getUserIds(); for (int user : users) { @@ -13947,7 +13946,8 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedUsers = new int[] {removeUser}; } // TODO: triage flags as part of 26466827 - final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int installerFlags = StorageManager.FLAG_STORAGE_CE + | StorageManager.FLAG_STORAGE_DE; try { mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags); } catch (InstallerException e) { @@ -14127,7 +14127,7 @@ public class PackageManagerService extends IPackageManager.Stub { // record of app. This helps users recover from UID mismatches without // resorting to a full data wipe. // TODO: triage flags as part of 26466827 - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; try { mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags); } catch (InstallerException e) { @@ -14362,7 +14362,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } // TODO: triage flags as part of 26466827 - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; try { mInstaller.clearAppData(p.volumeUuid, packageName, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY); @@ -14463,7 +14463,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // TODO: triage flags as part of 26466827 - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; try { mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath, libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); @@ -16787,16 +16787,20 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } // Reconcile app data for all started/unlocked users + final StorageManager sm = mContext.getSystemService(StorageManager.class); final UserManager um = mContext.getSystemService(UserManager.class); for (UserInfo user : um.getUsers()) { + final int flags; if (um.isUserUnlocked(user.id)) { - reconcileAppsData(volumeUuid, user.id, - Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE); + flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } else if (um.isUserRunning(user.id)) { - reconcileAppsData(volumeUuid, user.id, Installer.FLAG_DE_STORAGE); + flags = StorageManager.FLAG_STORAGE_DE; } else { continue; } + + sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags); + reconcileAppsData(volumeUuid, user.id, flags); } synchronized (mPackages) { @@ -16905,20 +16909,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } } - - final StorageManager sm = mContext.getSystemService(StorageManager.class); - final UserManager um = mContext.getSystemService(UserManager.class); - for (UserInfo user : um.getUsers()) { - final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id); - if (userDir.exists()) continue; - - try { - sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral()); - UserManagerService.enforceSerialNumber(userDir, user.serialNumber); - } catch (IOException e) { - Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e); - } - } } private void assertPackageKnown(String volumeUuid, String packageName) @@ -16988,7 +16978,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); * Verifies that directories exist and that ownership and labeling is * correct for all installed apps on all mounted volumes. */ - void reconcileAppsData(int userId, @StorageFlags int flags) { + void reconcileAppsData(int userId, int flags) { final StorageManager storage = mContext.getSystemService(StorageManager.class); for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { final String volumeUuid = vol.getFsUuid(); @@ -17005,7 +16995,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); * Verifies that directories exist and that ownership and labeling is * correct for all installed apps. */ - private void reconcileAppsData(String volumeUuid, int userId, @StorageFlags int flags) { + private void reconcileAppsData(String volumeUuid, int userId, int flags) { Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x" + Integer.toHexString(flags)); @@ -17016,7 +17006,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // First look for stale data that doesn't belong, and check if things // have changed since we did our last restorecon - if ((flags & Installer.FLAG_CE_STORAGE) != 0) { + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { if (!isUserKeyUnlocked(userId)) { throw new RuntimeException( "Yikes, someone asked us to reconcile CE storage while " + userId @@ -17034,12 +17024,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e); synchronized (mInstallLock) { destroyAppDataLI(volumeUuid, packageName, userId, - Installer.FLAG_CE_STORAGE); + StorageManager.FLAG_STORAGE_CE); } } } } - if ((flags & Installer.FLAG_DE_STORAGE) != 0) { + if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) { restoreconNeeded |= SELinuxMMAC.isRestoreconNeeded(deDir); final File[] files = FileUtils.listFilesOrEmpty(deDir); @@ -17051,7 +17041,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e); synchronized (mInstallLock) { destroyAppDataLI(volumeUuid, packageName, userId, - Installer.FLAG_DE_STORAGE); + StorageManager.FLAG_STORAGE_DE); } } } @@ -17080,10 +17070,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } if (restoreconNeeded) { - if ((flags & Installer.FLAG_CE_STORAGE) != 0) { + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { SELinuxMMAC.setRestoreconDone(ceDir); } - if ((flags & Installer.FLAG_DE_STORAGE) != 0) { + if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) { SELinuxMMAC.setRestoreconDone(deDir); } } @@ -17112,9 +17102,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); for (UserInfo user : um.getUsers()) { final int flags; if (um.isUserUnlocked(user.id)) { - flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE; + flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } else if (um.isUserRunning(user.id)) { - flags = Installer.FLAG_DE_STORAGE; + flags = StorageManager.FLAG_STORAGE_DE; } else { continue; } @@ -17135,7 +17125,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); * will try recovering system apps by wiping data; third-party app data is * left intact. */ - private void prepareAppData(String volumeUuid, int userId, @StorageFlags int flags, + private void prepareAppData(String volumeUuid, int userId, int flags, PackageParser.Package pkg, boolean restoreconNeeded) { if (DEBUG_APP_DATA) { Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x" @@ -17173,7 +17163,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); restoreconAppDataLI(volumeUuid, packageName, userId, flags, appId, app.seinfo); } - if ((flags & Installer.FLAG_CE_STORAGE) != 0) { + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { // Create a native library symlink only if we have native libraries // and if the native libraries are 32 bit libraries. We do not provide // this symlink for 64 bit libraries. diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index bbbe6936a4e6..fcb777b8e7fa 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3787,7 +3787,7 @@ final class Settings { continue; } // TODO: triage flags! - final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; try { installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i], seinfos[i], targetSdkVersions[i]); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 5f4656742e05..3cc7b10c6f24 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -25,7 +25,6 @@ import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IStopUserCallback; -import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -78,7 +77,9 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; -import com.android.server.pm.Installer.StorageFlags; + +import libcore.io.IoUtils; +import libcore.util.Objects; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -96,9 +97,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import libcore.io.IoUtils; -import libcore.util.Objects; - /** * Service for {@link UserManager}. * @@ -1893,17 +1891,8 @@ public class UserManagerService extends IUserManager.Stub { } final StorageManager storage = mContext.getSystemService(StorageManager.class); storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral()); - for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { - final String volumeUuid = vol.getFsUuid(); - try { - final File userDir = Environment.getDataUserDirectory(volumeUuid, userId); - storage.prepareUserStorage( - volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral()); - enforceSerialNumber(userDir, userInfo.serialNumber); - } catch (IOException e) { - Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e); - } - } + prepareUserStorage(userId, userInfo.serialNumber, + StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); mPm.createNewUser(userId); userInfo.partial = false; synchronized (mPackagesLock) { @@ -2466,11 +2455,24 @@ public class UserManagerService extends IUserManager.Stub { } /** + * Prepare storage areas for given user on all mounted devices. + */ + private void prepareUserStorage(int userId, int userSerial, int flags) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { + final String volumeUuid = vol.getFsUuid(); + storage.prepareUserStorage(volumeUuid, userId, userSerial, flags); + } + } + + /** * Called right before a user is started. This gives us a chance to prepare * app storage and apply any user restrictions. */ public void onBeforeStartUser(int userId) { - mPm.reconcileAppsData(userId, Installer.FLAG_DE_STORAGE); + final int userSerial = getUserSerialNumber(userId); + prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE); + mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE); if (userId != UserHandle.USER_SYSTEM) { synchronized (mRestrictionsLock) { @@ -2484,7 +2486,9 @@ public class UserManagerService extends IUserManager.Stub { * app storage. */ public void onBeforeUnlockUser(int userId) { - mPm.reconcileAppsData(userId, Installer.FLAG_CE_STORAGE); + final int userSerial = getUserSerialNumber(userId); + prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE); + mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE); } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 43b82e9fcd11..a92cc31659e6 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -6877,7 +6877,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID); final boolean freeformStackVisible = mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID); - final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible; + final boolean resizing = mWindowManagerInternal.isDockedDividerResizing(); + + // We need to force system bars when the docked stack is visible, when the freeform stack + // is visible but also when we are resizing for the transitions when docked stack + // visibility changes. + final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing; final boolean forceOpaqueSystemBars = forceShowSystemBars && !mForceStatusBarFromKeyguard; // apply translucent bar vis flags diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index b065e855915d..342c078f8162 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -584,14 +584,14 @@ public final class TvInputManagerService extends SystemService { for (IBinder sessionToken : serviceState.sessionTokens) { SessionState sessionState = userState.sessionStateMap.get(sessionToken); if (sessionState.session == null && (inputId == null - || sessionState.info.getId().equals(inputId))) { + || sessionState.inputId.equals(inputId))) { sessionsToAbort.add(sessionState); } } for (SessionState sessionState : sessionsToAbort) { removeSessionStateLocked(sessionState.sessionToken, sessionState.userId); sendSessionTokenToClientLocked(sessionState.client, - sessionState.info.getId(), null, null, sessionState.seq); + sessionState.inputId, null, null, sessionState.seq); } updateServiceConnectionLocked(serviceState.component, userId); } @@ -601,7 +601,7 @@ public final class TvInputManagerService extends SystemService { UserState userState = getOrCreateUserStateLocked(userId); SessionState sessionState = userState.sessionStateMap.get(sessionToken); if (DEBUG) { - Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")"); + Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")"); } InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); @@ -611,14 +611,14 @@ public final class TvInputManagerService extends SystemService { // Create a session. When failed, send a null token immediately. try { if (sessionState.isRecordingSession) { - service.createRecordingSession(callback, sessionState.info.getId()); + service.createRecordingSession(callback, sessionState.inputId); } else { - service.createSession(channels[1], callback, sessionState.info.getId()); + service.createSession(channels[1], callback, sessionState.inputId); } } catch (RemoteException e) { Slog.e(TAG, "error in createSession", e); removeSessionStateLocked(sessionToken, userId); - sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null, + sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null, null, sessionState.seq); } channels[1].dispose(); @@ -684,14 +684,11 @@ public final class TvInputManagerService extends SystemService { } } - TvInputInfo info = sessionState.info; - if (info != null) { - ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); - if (serviceState != null) { - serviceState.sessionTokens.remove(sessionToken); - } + ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName); + if (serviceState != null) { + serviceState.sessionTokens.remove(sessionToken); } - updateServiceConnectionLocked(sessionState.info.getComponent(), userId); + updateServiceConnectionLocked(sessionState.componentName, userId); // Log the end of watch. SomeArgs args = SomeArgs.obtain(); @@ -707,7 +704,7 @@ public final class TvInputManagerService extends SystemService { sessionState = getSessionStateLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID, userId); } - ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId); + ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId); if (!serviceState.isHardware) { return; } @@ -1091,8 +1088,9 @@ public final class TvInputManagerService extends SystemService { // Create a new session token and a session state. IBinder sessionToken = new Binder(); - SessionState sessionState = new SessionState(sessionToken, info, - isRecordingSession, client, seq, callingUid, resolvedUserId); + SessionState sessionState = new SessionState(sessionToken, info.getId(), + info.getComponent(), isRecordingSession, client, seq, callingUid, + resolvedUserId); // Add them to the global session state map of the current user. userState.sessionStateMap.put(sessionToken, sessionState); @@ -1273,7 +1271,7 @@ public final class TvInputManagerService extends SystemService { // Log the start of watch. SomeArgs args = SomeArgs.obtain(); - args.arg1 = sessionState.info.getComponent().getPackageName(); + args.arg1 = sessionState.componentName.getPackageName(); args.arg2 = System.currentTimeMillis(); args.arg3 = ContentUris.parseId(channelUri); args.arg4 = params; @@ -1779,10 +1777,10 @@ public final class TvInputManagerService extends SystemService { return false; } for (SessionState sessionState : userState.sessionStateMap.values()) { - if (sessionState.info.getId().equals(inputId) + if (sessionState.inputId.equals(inputId) && sessionState.hardwareSessionToken != null) { hardwareInputId = userState.sessionStateMap.get( - sessionState.hardwareSessionToken).info.getId(); + sessionState.hardwareSessionToken).inputId; break; } } @@ -1918,7 +1916,7 @@ public final class TvInputManagerService extends SystemService { pw.println(entry.getKey() + ": " + session); pw.increaseIndent(); - pw.println("info: " + session.info); + pw.println("inputId: " + session.inputId); pw.println("client: " + session.client); pw.println("seq: " + session.seq); pw.println("callingUid: " + session.callingUid); @@ -2046,7 +2044,8 @@ public final class TvInputManagerService extends SystemService { } private final class SessionState implements IBinder.DeathRecipient { - private final TvInputInfo info; + private final String inputId; + private final ComponentName componentName; private final boolean isRecordingSession; private final ITvInputClient client; private final int seq; @@ -2058,10 +2057,12 @@ public final class TvInputManagerService extends SystemService { // Not null if this session represents an external device connected to a hardware TV input. private IBinder hardwareSessionToken; - private SessionState(IBinder sessionToken, TvInputInfo info, boolean isRecordingSession, - ITvInputClient client, int seq, int callingUid, int userId) { + private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, + boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, + int userId) { this.sessionToken = sessionToken; - this.info = info; + this.inputId = inputId; + this.componentName = componentName; this.isRecordingSession = isRecordingSession; this.client = client; this.seq = seq; @@ -2274,19 +2275,19 @@ public final class TvInputManagerService extends SystemService { @Override public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) { if (DEBUG) { - Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.info.getId() + ")"); + Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")"); } synchronized (mLock) { mSessionState.session = session; mSessionState.hardwareSessionToken = hardwareSessionToken; if (session != null && addSessionTokenToClientStateLocked(session)) { sendSessionTokenToClientLocked(mSessionState.client, - mSessionState.info.getId(), mSessionState.sessionToken, mChannels[0], + mSessionState.inputId, mSessionState.sessionToken, mChannels[0], mSessionState.seq); } else { removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId); sendSessionTokenToClientLocked(mSessionState.client, - mSessionState.info.getId(), null, null, mSessionState.seq); + mSessionState.inputId, null, null, mSessionState.seq); } mChannels[0].dispose(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index be1b85c2f9b5..a06d3fc74393 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -289,11 +289,9 @@ class Task implements DimLayer.DimLayerUser { if (displayContent != null) { displayContent.getLogicalDisplayRect(mTmpRect); rotation = displayContent.getDisplayInfo().rotation; - if (bounds == null) { + mFullscreen = bounds == null; + if (mFullscreen) { bounds = mTmpRect; - mFullscreen = true; - } else { - mFullscreen = mTmpRect.equals(bounds); } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index ecc13648aeb7..40ca1c5a688c 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -255,11 +255,9 @@ public class TaskStack implements DimLayer.DimLayerUser, if (mDisplayContent != null) { mDisplayContent.getLogicalDisplayRect(mTmpRect); rotation = mDisplayContent.getDisplayInfo().rotation; - if (bounds == null) { + mFullscreen = bounds == null; + if (mFullscreen) { bounds = mTmpRect; - mFullscreen = true; - } else { - mFullscreen = mTmpRect.equals(bounds); } } @@ -587,7 +585,8 @@ public class TaskStack implements DimLayer.DimLayerUser, } void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) { - if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) { + if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) + || mDisplayContent == null) { outBounds.set(mBounds); return; } @@ -616,8 +615,7 @@ public class TaskStack implements DimLayer.DimLayerUser, mDisplayContent.getLogicalDisplayRect(mTmpRect); dockedStack.getRawBounds(mTmpRect2); - final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP - || dockedSide == DOCKED_LEFT; + final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2, mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); @@ -722,6 +720,19 @@ public class TaskStack implements DimLayer.DimLayerUser, } } + void resetDockedStackToMiddle() { + if (mStackId != DOCKED_STACK_ID) { + throw new IllegalStateException("Not a docked stack=" + this); + } + + mService.mDockedStackCreateBounds = null; + + final Rect bounds = new Rect(); + getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/); + mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID, + 1 /*allowResizeInDockedMode*/, bounds).sendToTarget(); + } + void detachDisplay() { EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); @@ -865,14 +876,14 @@ public class TaskStack implements DimLayer.DimLayerUser, final int orientation = mService.mCurConfiguration.orientation; if (orientation == Configuration.ORIENTATION_PORTRAIT) { // Portrait mode, docked either at the top or the bottom. - if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) { + if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) { return DOCKED_TOP; } else { return DOCKED_BOTTOM; } } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { // Landscape mode, docked either on the left or on the right. - if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) { + if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) { return DOCKED_LEFT; } else { return DOCKED_RIGHT; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a26430e773cc..ae6c89a4d610 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -464,6 +464,8 @@ public class WindowManagerService extends IWindowManager.Stub EmulatorDisplayOverlay mEmulatorDisplayOverlay; final float[] mTmpFloats = new float[9]; + final Rect mTmpRect = new Rect(); + final Rect mTmpRect2 = new Rect(); boolean mDisplayReady; boolean mSafeMode; @@ -4845,17 +4847,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - /** Returns true if the input bounds corresponds to the fullscreen bounds the stack is on. */ - public boolean isFullscreenBounds(int stackId, Rect bounds) { - synchronized (mWindowMap) { - final TaskStack stack = mStackIdToStack.get(stackId); - if (stack == null || bounds == null) { - return true; - } - return stack.isFullscreenBounds(bounds); - } - } - /** * Re-sizes a stack and its containing tasks. * @param stackId Id of stack to resize. @@ -5373,8 +5364,18 @@ public class WindowManagerService extends IWindowManager.Stub mWindowPlacerLocked.performSurfacePlacement(); // Notify whether the docked stack exists for the current user - getDefaultDisplayContentLocked().mDividerControllerLocked + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + displayContent.mDividerControllerLocked .notifyDockedStackExistsChanged(hasDockedTasksForUser(newUserId)); + + // If the display is already prepared, update the density. + // Otherwise, we'll update it when it's prepared. + if (mDisplayReady) { + final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId); + final int targetDensity = forcedDensity != 0 ? forcedDensity + : displayContent.mInitialDisplayDensity; + setForcedDisplayDensityLocked(displayContent, targetDensity); + } } } @@ -8370,21 +8371,9 @@ public class WindowManagerService extends IWindowManager.Stub } // Display density. - String densityStr = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.DISPLAY_DENSITY_FORCED); - if (densityStr == null || densityStr.length() == 0) { - densityStr = SystemProperties.get(DENSITY_OVERRIDE, null); - } - if (densityStr != null && densityStr.length() > 0) { - int density; - try { - density = Integer.parseInt(densityStr); - if (displayContent.mBaseDisplayDensity != density) { - Slog.i(TAG_WM, "FORCED DISPLAY DENSITY: " + density); - displayContent.mBaseDisplayDensity = density; - } - } catch (NumberFormatException ex) { - } + final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); + if (density != 0) { + displayContent.mBaseDisplayDensity = density; } // Display scaling mode. @@ -8470,8 +8459,9 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null) { setForcedDisplayDensityLocked(displayContent, density); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density)); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, + Integer.toString(density), mCurrentUserId); } } } finally { @@ -8479,13 +8469,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - // displayContent must not be null - private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) { - Slog.i(TAG_WM, "Using new display density: " + density); - displayContent.mBaseDisplayDensity = density; - reconfigureDisplayLocked(displayContent); - } - @Override public void clearForcedDisplayDensity(int displayId) { if (mContext.checkCallingOrSelfPermission( @@ -8504,8 +8487,8 @@ public class WindowManagerService extends IWindowManager.Stub if (displayContent != null) { setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_DENSITY_FORCED, ""); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, "", mCurrentUserId); } } } finally { @@ -8513,6 +8496,38 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * @param userId the ID of the user + * @return the forced display density for the specified user, if set, or + * {@code 0} if not set + */ + private int getForcedDisplayDensityForUserLocked(int userId) { + String densityStr = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, userId); + if (densityStr == null || densityStr.length() == 0) { + densityStr = SystemProperties.get(DENSITY_OVERRIDE, null); + } + if (densityStr != null && densityStr.length() > 0) { + try { + return Integer.parseInt(densityStr); + } catch (NumberFormatException ex) { + } + } + return 0; + } + + /** + * Forces the given display to the use the specified density. + * + * @param displayContent the display to modify + * @param density the density in DPI to use + */ + private void setForcedDisplayDensityLocked(@NonNull DisplayContent displayContent, + int density) { + displayContent.mBaseDisplayDensity = density; + reconfigureDisplayLocked(displayContent); + } + // displayContent must not be null private void reconfigureDisplayLocked(DisplayContent displayContent) { // TODO: Multidisplay: for now only use with default display. @@ -10394,8 +10409,28 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void getStableInsets(Rect outInsets) throws RemoteException { synchronized (mWindowMap) { + getStableInsetsLocked(outInsets); + } + } + + private void getStableInsetsLocked(Rect outInsets) { + final DisplayInfo di = getDefaultDisplayInfoLocked(); + mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets); + } + + /** + * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable + * inset areas. + * + * @param inOutBounds The inOutBounds to subtract the stable inset areas from. + */ + public void subtractStableInsets(Rect inOutBounds) { + synchronized (mWindowMap) { + getStableInsetsLocked(mTmpRect2); final DisplayInfo di = getDefaultDisplayInfoLocked(); - mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets); + mTmpRect.set(0, 0, di.logicalWidth, di.logicalHeight); + mTmpRect.inset(mTmpRect2); + inOutBounds.intersect(mTmpRect); } } @@ -10599,5 +10634,12 @@ public class WindowManagerService extends IWindowManager.Stub return WindowManagerService.this.isStackVisibleLocked(stackId); } } + + @Override + public boolean isDockedDividerResizing() { + synchronized (mWindowMap) { + return getDefaultDisplayContentLocked().getDockedDividerController().isResizing(); + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 465c7e0937b6..880514cd25f6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import com.android.server.input.InputWindowHandle; - import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; @@ -53,10 +51,13 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerPolicy; +import com.android.server.input.InputWindowHandle; + import java.io.PrintWriter; import java.util.ArrayList; import static android.app.ActivityManager.StackId; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; @@ -75,8 +76,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; @@ -1259,8 +1260,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { * it may obscure windows behind it. */ boolean isOpaqueDrawn() { - return (mAttrs.format == PixelFormat.OPAQUE - || mAttrs.type == TYPE_WALLPAPER) + // When there is keyguard, wallpaper could be placed over the secure app + // window but invisible. We need to check wallpaper visibility explicitly + // to determine if it's occluding apps. + return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE) + || (mIsWallpaper && mWallpaperVisible)) && isDrawnLw() && mWinAnimator.mAnimation == null && (mAppToken == null || mAppToken.mAppAnimator.animation == null); } @@ -1608,6 +1612,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { win.mAppToken.appDied = true; } mService.removeWindowLocked(win); + if (win.mAttrs.type == TYPE_DOCK_DIVIDER) { + // The owner of the docked divider died :( We reset the docked stack, + // just in case they have the divider at an unstable position. + final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID); + if (stack != null) { + stack.resetDockedStackToMiddle(); + } + } } else if (mHasSurface) { Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid."); mService.removeWindowLocked(WindowState.this); @@ -2133,7 +2145,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { // background. return (mDisplayContent.mDividerControllerLocked.isResizing() || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) && - !task.inFreeformWorkspace() && !task.isFullscreen(); + !task.inFreeformWorkspace(); + } void setDragResizing() { @@ -2327,6 +2340,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (mDrawLock != null) { pw.print(prefix); pw.println("mDrawLock=" + mDrawLock); } + if (isDragResizing()) { + pw.print(prefix); pw.println("isDragResizing=" + isDragResizing()); + } + if (computeDragResizing()) { + pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing()); + } } String makeInputChannelName() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 908d2f0e8867..f296d68cedcf 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4132,7 +4132,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias, final IBinder response) { // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers. - if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) { + if (!isCallerWithSystemUid()) { return; } @@ -5860,8 +5860,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null); - if (hasUserSetupCompleted(userHandle) - && !UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) { + if (hasUserSetupCompleted(userHandle) && !isCallerWithSystemUid()) { throw new IllegalStateException("Cannot set the profile owner on a user which is " + "already set-up"); } @@ -5921,8 +5920,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void enforceManageUsers() { final int callingUid = mInjector.binderGetCallingUid(); - if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) - || callingUid == Process.ROOT_UID)) { + if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); } } @@ -5945,8 +5943,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (userHandle == UserHandle.getUserId(callingUid)) { return; } - if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) - || callingUid == Process.ROOT_UID)) { + if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) { mContext.enforceCallingOrSelfPermission(permission, "Must be system or have " + permission + " permission"); } @@ -5964,6 +5961,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private boolean isCallerWithSystemUid() { + return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID); + } + private int getProfileParentId(int userHandle) { final long ident = mInjector.binderClearCallingIdentity(); try { @@ -6248,7 +6249,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public ComponentName getRestrictionsProvider(int userHandle) { synchronized (this) { - if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) { + if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query the permission provider"); } DevicePolicyData userData = getUserData(userHandle); @@ -6321,8 +6322,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * permittedList or are a system app. */ private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages, - List<String> permittedList) { - int userIdToCheck = UserHandle.getCallingUserId(); + List<String> permittedList, int userIdToCheck) { long id = mInjector.binderClearCallingIdentity(); try { // If we have an enabled packages list for a managed profile the packages @@ -6389,7 +6389,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { for (AccessibilityServiceInfo service : enabledServices) { enabledPackages.add(service.getResolveInfo().serviceInfo.packageName); } - if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) { + if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, + userId)) { Slog.e(LOG_TAG, "Cannot set permitted accessibility services, " + "because it contains already enabled accesibility services."); return false; @@ -6481,6 +6482,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override + public boolean isAccessibilityServicePermittedByAdmin(ComponentName who, String packageName, + int userHandle) { + if (!mHasFeature) { + return true; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + Preconditions.checkStringNotEmpty(packageName, "packageName is null"); + if (!isCallerWithSystemUid()){ + throw new SecurityException( + "Only the system can query if an accessibility service is disabled by admin"); + } + synchronized (this) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); + if (admin.permittedAccessiblityServices == null) { + return true; + } + return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName), + admin.permittedAccessiblityServices, userHandle); + } + } + private boolean checkCallerIsCurrentUserOrProfile() { int callingUserId = UserHandle.getCallingUserId(); long token = mInjector.binderClearCallingIdentity(); @@ -6536,7 +6559,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { for (InputMethodInfo ime : enabledImes) { enabledPackages.add(ime.getPackageName()); } - if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) { + if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, + mInjector.binderGetCallingUserHandle().getIdentifier())) { Slog.e(LOG_TAG, "Cannot set permitted input methods, " + "because it contains already enabled input method."); return false; @@ -6629,6 +6653,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName, + int userHandle) { + if (!mHasFeature) { + return true; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + Preconditions.checkStringNotEmpty(packageName, "packageName is null"); + if (!isCallerWithSystemUid()) { + throw new SecurityException( + "Only the system can query if an input method is disabled by admin"); + } + synchronized (this) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); + if (admin.permittedInputMethods == null) { + return true; + } + return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName), + admin.permittedInputMethods, userHandle); + } + } + + @Override public UserHandle createUser(ComponentName who, String name) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { @@ -7425,7 +7471,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { - if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) { + if (!isCallerWithSystemUid()) { throw new SecurityException("notifyLockTaskModeChanged can only be called by system"); } synchronized (this) { @@ -8180,7 +8226,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); - if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) { + if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query support message for user"); } synchronized (this) { @@ -8198,7 +8244,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); - if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) { + if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query support message for user"); } synchronized (this) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b64db578decb..0cf93288714f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -496,6 +496,7 @@ public final class SystemServer { boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false); boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false); boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false); + boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false); boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); try { @@ -788,7 +789,9 @@ public final class SystemServer { mSystemServiceManager.startService( "com.android.server.wifi.WifiScanningService"); - mSystemServiceManager.startService("com.android.server.wifi.RttService"); + if (!disableRtt) { + mSystemServiceManager.startService("com.android.server.wifi.RttService"); + } if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) || mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) { @@ -1088,7 +1091,9 @@ public final class SystemServer { mSystemServiceManager.startService(TrustManagerService.class); - mSystemServiceManager.startService(FingerprintService.class); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + mSystemServiceManager.startService(FingerprintService.class); + } traceBeginAndSlog("StartBackgroundDexOptService"); try { diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 25cb64c94215..071ec1b025f0 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -15,6 +15,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ services.core \ services.devicepolicy \ services.net \ + services.usage \ easymocklib \ guava \ android-support-test \ @@ -26,7 +27,9 @@ LOCAL_PACKAGE_NAME := FrameworksServicesTests LOCAL_CERTIFICATE := platform -LOCAL_JNI_SHARED_LIBRARIES := libapfjni +LOCAL_JNI_SHARED_LIBRARIES := \ + libapfjni \ + libnativehelper include $(BUILD_PACKAGE) diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java index 3dc1a9ab7847..53ca45dde5ae 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java @@ -29,6 +29,7 @@ import android.test.AndroidTestCase; import java.io.File; import java.util.List; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; @@ -135,8 +136,7 @@ public abstract class DpmTestBase extends AndroidTestCase { doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceiversAsUser( MockUtils.checkIntentComponent(admin), - eq(PackageManager.GET_META_DATA - | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS), + anyInt(), eq(UserHandle.getUserId(packageUid))); // Set up getPackageInfo(). diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java new file mode 100644 index 000000000000..9ccb1a618568 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usage; + +import android.os.FileUtils; +import android.test.AndroidTestCase; + +import java.io.File; + +public class AppIdleHistoryTests extends AndroidTestCase { + + File mStorageDir; + + final static String PACKAGE_1 = "com.android.testpackage1"; + final static String PACKAGE_2 = "com.android.testpackage2"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mStorageDir = new File(getContext().getFilesDir(), "appidle"); + mStorageDir.mkdirs(); + } + + @Override + protected void tearDown() throws Exception { + FileUtils.deleteContents(mStorageDir); + super.tearDown(); + } + + public void testFilesCreation() { + final int userId = 0; + AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0); + + aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000); + aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000); + // Screen On time file should be written right away + assertTrue(aih.getScreenOnTimeFile().exists()); + + aih.writeAppIdleTimesLocked(userId); + // stats file should be written now + assertTrue(new File(new File(mStorageDir, "users/" + userId), + AppIdleHistory.APP_IDLE_FILENAME).exists()); + } + + public void testScreenOnTime() { + AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000); + aih.updateDisplayLocked(false, 2000); + assertEquals(aih.getScreenOnTimeLocked(2000), 0); + aih.updateDisplayLocked(true, 3000); + assertEquals(aih.getScreenOnTimeLocked(4000), 1000); + assertEquals(aih.getScreenOnTimeLocked(5000), 2000); + aih.updateDisplayLocked(false, 6000); + // Screen on time should not keep progressing with screen is off + assertEquals(aih.getScreenOnTimeLocked(7000), 3000); + assertEquals(aih.getScreenOnTimeLocked(8000), 3000); + aih.writeElapsedTimeLocked(); + + // Check if the screen on time is persisted across instantiations + AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0); + assertEquals(aih2.getScreenOnTimeLocked(11000), 3000); + aih2.updateDisplayLocked(true, 4000); + aih2.updateDisplayLocked(false, 5000); + assertEquals(aih2.getScreenOnTimeLocked(13000), 4000); + } + + public void testPackageEvents() { + AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000); + aih.setThresholds(4000, 1000); + aih.updateDisplayLocked(true, 1000); + // App is not-idle by default + assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500)); + // Still not idle + assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000)); + // Idle now + assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000)); + // Not idle + assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000)); + + // Screen off + aih.updateDisplayLocked(false, 9100); + // Still idle after 10 seconds because screen hasn't been on long enough + assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000)); + aih.updateDisplayLocked(true, 21000); + assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000)); + } +}
\ No newline at end of file diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index e3c0868ea3a2..3e2b43d2af5c 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -16,19 +16,45 @@ package com.android.server.usage; +import android.os.Environment; +import android.os.SystemClock; +import android.os.UserHandle; import android.util.ArrayMap; +import android.util.AtomicFile; +import android.util.Slog; import android.util.SparseArray; +import android.util.TimeUtils; +import android.util.Xml; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + /** * Keeps track of recent active state changes in apps. * Access should be guarded by a lock by the caller. */ public class AppIdleHistory { - private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>(); - private long lastPeriod = 0; + private static final String TAG = "AppIdleHistory"; + + // History for all users and all packages + private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>(); + private long mLastPeriod = 0; private static final long ONE_MINUTE = 60 * 1000; private static final int HISTORY_SIZE = 100; private static final int FLAG_LAST_STATE = 2; @@ -36,77 +62,353 @@ public class AppIdleHistory { private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE : 60 * ONE_MINUTE; - public void addEntry(String packageName, int userId, boolean idle, long timeNow) { - ArrayMap<String, byte[]> userHistory = getUserHistory(userId); - byte[] packageHistory = getPackageHistory(userHistory, packageName); + @VisibleForTesting + static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; + private static final String TAG_PACKAGES = "packages"; + private static final String TAG_PACKAGE = "package"; + private static final String ATTR_NAME = "name"; + // Screen on timebase time when app was last used + private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; + // Elapsed timebase time when app was last used + private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; + + // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) + private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration + private long mElapsedDuration; // Total device on duration since device was "born" + + // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot) + private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration + private long mScreenOnDuration; // Total screen on duration since device was "born" + + private long mElapsedTimeThreshold; + private long mScreenOnTimeThreshold; + private final File mStorageDir; + + private boolean mScreenOn; + + private static class PackageHistory { + final byte[] recent = new byte[HISTORY_SIZE]; + long lastUsedElapsedTime; + long lastUsedScreenTime; + } + + AppIdleHistory(long elapsedRealtime) { + this(Environment.getDataSystemDirectory(), elapsedRealtime); + } + + @VisibleForTesting + AppIdleHistory(File storageDir, long elapsedRealtime) { + mElapsedSnapshot = elapsedRealtime; + mScreenOnSnapshot = elapsedRealtime; + mStorageDir = storageDir; + readScreenOnTimeLocked(); + } + + public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) { + mElapsedTimeThreshold = elapsedTimeThreshold; + mScreenOnTimeThreshold = screenOnTimeThreshold; + } + + public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) { + if (screenOn == mScreenOn) return; + + mScreenOn = screenOn; + if (mScreenOn) { + mScreenOnSnapshot = elapsedRealtime; + } else { + mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot; + mElapsedDuration += elapsedRealtime - mElapsedSnapshot; + writeScreenOnTimeLocked(); + mElapsedSnapshot = elapsedRealtime; + } + } + + public long getScreenOnTimeLocked(long elapsedRealtime) { + long screenOnTime = mScreenOnDuration; + if (mScreenOn) { + screenOnTime += elapsedRealtime - mScreenOnSnapshot; + } + return screenOnTime; + } + + @VisibleForTesting + File getScreenOnTimeFile() { + return new File(mStorageDir, "screen_on_time"); + } + + private void readScreenOnTimeLocked() { + File screenOnTimeFile = getScreenOnTimeFile(); + if (screenOnTimeFile.exists()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); + mScreenOnDuration = Long.parseLong(reader.readLine()); + mElapsedDuration = Long.parseLong(reader.readLine()); + reader.close(); + } catch (IOException | NumberFormatException e) { + } + } else { + writeScreenOnTimeLocked(); + } + } + + private void writeScreenOnTimeLocked() { + AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); + FileOutputStream fos = null; + try { + fos = screenOnTimeFile.startWrite(); + fos.write((Long.toString(mScreenOnDuration) + "\n" + + Long.toString(mElapsedDuration) + "\n").getBytes()); + screenOnTimeFile.finishWrite(fos); + } catch (IOException ioe) { + screenOnTimeFile.failWrite(fos); + } + } + + /** + * To be called periodically to keep track of elapsed time when app idle times are written + */ + public void writeElapsedTimeLocked() { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + // Only bump up and snapshot the elapsed time. Don't change screen on duration. + mElapsedDuration += elapsedRealtime - mElapsedSnapshot; + mElapsedSnapshot = elapsedRealtime; + writeScreenOnTimeLocked(); + } + + public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) { + ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); + PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, + elapsedRealtime); + + shiftHistoryToNow(userHistory, elapsedRealtime); + + packageHistory.lastUsedElapsedTime = mElapsedDuration + + (elapsedRealtime - mElapsedSnapshot); + packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime); + packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; + } + + public void setIdle(String packageName, int userId, long elapsedRealtime) { + ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); + PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, + elapsedRealtime); + + shiftHistoryToNow(userHistory, elapsedRealtime); - long thisPeriod = timeNow / PERIOD_DURATION; + packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; + } + + private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory, + long elapsedRealtime) { + long thisPeriod = elapsedRealtime / PERIOD_DURATION; // Has the period switched over? Slide all users' package histories - if (lastPeriod != 0 && lastPeriod < thisPeriod - && (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) { - int diff = (int) (thisPeriod - lastPeriod); + if (mLastPeriod != 0 && mLastPeriod < thisPeriod + && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { + int diff = (int) (thisPeriod - mLastPeriod); final int NUSERS = mIdleHistory.size(); for (int u = 0; u < NUSERS; u++) { userHistory = mIdleHistory.valueAt(u); - for (byte[] history : userHistory.values()) { + for (PackageHistory idleState : userHistory.values()) { // Shift left - System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff); + System.arraycopy(idleState.recent, diff, idleState.recent, 0, + HISTORY_SIZE - diff); // Replicate last state across the diff for (int i = 0; i < diff; i++) { - history[HISTORY_SIZE - i - 1] = - (byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); + idleState.recent[HISTORY_SIZE - i - 1] = + (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); } } } } - lastPeriod = thisPeriod; - if (!idle) { - packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; - } else { - packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; - } + mLastPeriod = thisPeriod; } - private ArrayMap<String, byte[]> getUserHistory(int userId) { - ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId); + private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) { + ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); if (userHistory == null) { userHistory = new ArrayMap<>(); mIdleHistory.put(userId, userHistory); + readAppIdleTimesLocked(userId, userHistory); } return userHistory; } - private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) { - byte[] packageHistory = userHistory.get(packageName); + private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory, + String packageName, long elapsedRealtime) { + PackageHistory packageHistory = userHistory.get(packageName); if (packageHistory == null) { - packageHistory = new byte[HISTORY_SIZE]; + packageHistory = new PackageHistory(); + packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime); + packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime); userHistory.put(packageName, packageHistory); } return packageHistory; } - public void removeUser(int userId) { + public void onUserRemoved(int userId) { mIdleHistory.remove(userId); } - public boolean isIdle(int userId, String packageName) { - ArrayMap<String, byte[]> userHistory = getUserHistory(userId); - byte[] packageHistory = getPackageHistory(userHistory, packageName); - return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0; + public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) { + ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); + PackageHistory packageHistory = + getPackageHistoryLocked(userHistory, packageName, elapsedRealtime); + if (packageHistory == null) { + return false; // Default to not idle + } else { + return hasPassedThresholdsLocked(packageHistory, elapsedRealtime); + } + } + + private long getElapsedTimeLocked(long elapsedRealtime) { + return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); + } + + public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) { + ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); + PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, + elapsedRealtime); + packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime) + - mElapsedTimeThreshold; + packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime) + - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */; + } + + private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) { + return (packageHistory.lastUsedScreenTime + <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold) + && (packageHistory.lastUsedElapsedTime + <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold); + } + + private File getUserFile(int userId) { + return new File(new File(new File(mStorageDir, "users"), + Integer.toString(userId)), APP_IDLE_FILENAME); + } + + private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) { + FileInputStream fis = null; + try { + AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); + fis = appIdleFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, StandardCharsets.UTF_8.name()); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // Skip + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(TAG, "Unable to read app idle file for user " + userId); + return; + } + if (!parser.getName().equals(TAG_PACKAGES)) { + return; + } + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG) { + final String name = parser.getName(); + if (name.equals(TAG_PACKAGE)) { + final String packageName = parser.getAttributeValue(null, ATTR_NAME); + PackageHistory packageHistory = new PackageHistory(); + packageHistory.lastUsedElapsedTime = + Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); + packageHistory.lastUsedScreenTime = + Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); + userHistory.put(packageName, packageHistory); + } + } + } + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Unable to read app idle file for user " + userId); + } finally { + IoUtils.closeQuietly(fis); + } + } + + public void writeAppIdleTimesLocked(int userId) { + FileOutputStream fos = null; + AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); + try { + fos = appIdleFile.startWrite(); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + FastXmlSerializer xml = new FastXmlSerializer(); + xml.setOutput(bos, StandardCharsets.UTF_8.name()); + xml.startDocument(null, true); + xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + xml.startTag(null, TAG_PACKAGES); + + ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId); + final int N = userHistory.size(); + for (int i = 0; i < N; i++) { + String packageName = userHistory.keyAt(i); + PackageHistory history = userHistory.valueAt(i); + xml.startTag(null, TAG_PACKAGE); + xml.attribute(null, ATTR_NAME, packageName); + xml.attribute(null, ATTR_ELAPSED_IDLE, + Long.toString(history.lastUsedElapsedTime)); + xml.attribute(null, ATTR_SCREEN_IDLE, + Long.toString(history.lastUsedScreenTime)); + xml.endTag(null, TAG_PACKAGE); + } + + xml.endTag(null, TAG_PACKAGES); + xml.endDocument(); + appIdleFile.finishWrite(fos); + } catch (Exception e) { + appIdleFile.failWrite(fos); + Slog.e(TAG, "Error writing app idle file for user " + userId); + } } public void dump(IndentingPrintWriter idpw, int userId) { - ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId); + idpw.println("Package idle stats:"); + idpw.increaseIndent(); + ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime); + final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime); + if (userHistory == null) return; + final int P = userHistory.size(); + for (int p = 0; p < P; p++) { + final String packageName = userHistory.keyAt(p); + final PackageHistory packageHistory = userHistory.valueAt(p); + idpw.print("package=" + packageName); + idpw.print(" lastUsedElapsed="); + TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw); + idpw.print(" lastUsedScreenOn="); + TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw); + idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n")); + idpw.println(); + } + idpw.println(); + idpw.print("totalElapsedTime="); + TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw); + idpw.println(); + idpw.print("totalScreenOnTime="); + TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw); + idpw.println(); + idpw.decreaseIndent(); + } + + public void dumpHistory(IndentingPrintWriter idpw, int userId) { + ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); + final long elapsedRealtime = SystemClock.elapsedRealtime(); if (userHistory == null) return; final int P = userHistory.size(); for (int p = 0; p < P; p++) { final String packageName = userHistory.keyAt(p); - final byte[] history = userHistory.valueAt(p); + final byte[] history = userHistory.valueAt(p).recent; for (int i = 0; i < HISTORY_SIZE; i++) { idpw.print(history[i] == 0 ? '.' : 'A'); } + idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" " + packageName); idpw.println(); } } -}
\ No newline at end of file +} diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 7f379fe34452..f541f70d4334 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -48,7 +48,6 @@ class IntervalStats { usageStats.mPackageName = getCachedStringRef(packageName); usageStats.mBeginTimeStamp = beginTime; usageStats.mEndTimeStamp = endTime; - usageStats.mBeginIdleTime = 0; packageStats.put(usageStats.mPackageName, usageStats); } return usageStats; @@ -113,7 +112,6 @@ class IntervalStats { if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) { usageStats.mLastTimeUsed = timeStamp; } - usageStats.mLastTimeSystemUsed = timeStamp; usageStats.mEndTimeStamp = timeStamp; if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) { @@ -123,22 +121,6 @@ class IntervalStats { endTime = timeStamp; } - /** - * Updates the last active time for the package. The timestamp uses a timebase that - * tracks the device usage time. - * @param packageName - * @param timeStamp - */ - void updateBeginIdleTime(String packageName, long timeStamp) { - UsageStats usageStats = getOrCreateUsageStats(packageName); - usageStats.mBeginIdleTime = timeStamp; - } - - void updateSystemLastUsedTime(String packageName, long lastUsedTime) { - UsageStats usageStats = getOrCreateUsageStats(packageName); - usageStats.mLastTimeSystemUsed = lastUsedTime; - } - void updateConfigurationStats(Configuration config, long timeStamp) { if (activeConfiguration != null) { ConfigurationStats activeStats = configurations.get(activeConfiguration); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 7774038740fe..46ad8a10534e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -40,6 +40,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.database.ContentObserver; import android.hardware.display.DisplayManager; @@ -62,7 +63,6 @@ import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.ArraySet; -import android.util.AtomicFile; import android.util.KeyValueListParser; import android.util.Slog; import android.util.SparseArray; @@ -77,12 +77,8 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; -import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -106,7 +102,7 @@ public class UsageStatsService extends SystemService implements private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES; private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds. - long mAppIdleDurationMillis; + long mAppIdleScreenThresholdMillis; long mCheckIdleIntervalMillis; long mAppIdleWallclockThresholdMillis; long mAppIdleParoleIntervalMillis; @@ -147,11 +143,8 @@ public class UsageStatsService extends SystemService implements private volatile boolean mPendingOneTimeCheckIdleStates; - long mScreenOnTime; - long mLastScreenOnEventRealtime; - @GuardedBy("mLock") - private AppIdleHistory mAppIdleHistory = new AppIdleHistory(); + private AppIdleHistory mAppIdleHistory; private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>(); @@ -191,8 +184,7 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { cleanUpRemovedUsersLocked(); - mLastScreenOnEventRealtime = SystemClock.elapsedRealtime(); - mScreenOnTime = readScreenOnTimeLocked(); + mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime()); } mRealTimeSnapshot = SystemClock.elapsedRealtime(); @@ -221,7 +213,7 @@ public class UsageStatsService extends SystemService implements mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); synchronized (mLock) { - updateDisplayLocked(); + mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime()); } if (mPendingOneTimeCheckIdleStates) { @@ -232,6 +224,11 @@ public class UsageStatsService extends SystemService implements } } + private boolean isDisplayOn() { + return mDisplayManager + .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON; + } + private class UserActionsReceiver extends BroadcastReceiver { @Override @@ -274,7 +271,8 @@ public class UsageStatsService extends SystemService implements @Override public void onDisplayChanged(int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { synchronized (UsageStatsService.this.mLock) { - updateDisplayLocked(); + mAppIdleHistory.updateDisplayLocked(isDisplayOn(), + SystemClock.elapsedRealtime()); } } } @@ -291,8 +289,25 @@ public class UsageStatsService extends SystemService implements } @Override - public long getAppIdleRollingWindowDurationMillis() { - return mAppIdleWallclockThresholdMillis * 2; + public void onNewUpdate(int userId) { + initializeDefaultsForSystemApps(userId); + } + + private void initializeDefaultsForSystemApps(int userId) { + Slog.d(TAG, "Initializing defaults for system apps on user " + userId); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser( + PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + userId); + final int packageCount = packages.size(); + for (int i = 0; i < packageCount; i++) { + final PackageInfo pi = packages.get(i); + String packageName = pi.packageName; + if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) { + mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime); + } + } } private void cleanUpRemovedUsersLocked() { @@ -350,7 +365,7 @@ public class UsageStatsService extends SystemService implements if (timeLeft < 0) { timeLeft = 0; } - mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10); + mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft); } private void postParoleEndTimeout() { @@ -400,28 +415,27 @@ public class UsageStatsService extends SystemService implements return; } + final long elapsedRealtime = SystemClock.elapsedRealtime(); for (int i = 0; i < userIds.length; i++) { final int userId = userIds[i]; List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser( - PackageManager.GET_DISABLED_COMPONENTS - | PackageManager.GET_UNINSTALLED_PACKAGES, + PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); synchronized (mLock) { - final long timeNow = checkAndGetTimeLocked(); - final long screenOnTime = getScreenOnTimeLocked(); - UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, - timeNow); final int packageCount = packages.size(); for (int p = 0; p < packageCount; p++) { final PackageInfo pi = packages.get(p); final String packageName = pi.packageName; final boolean isIdle = isAppIdleFiltered(packageName, UserHandle.getAppId(pi.applicationInfo.uid), - userId, service, timeNow, screenOnTime); + userId, elapsedRealtime); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, isIdle ? 1 : 0, packageName)); - mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow); + if (isIdle) { + mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime); + } } } } @@ -458,62 +472,6 @@ public class UsageStatsService extends SystemService implements } } - void updateDisplayLocked() { - boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState() - == Display.STATE_ON; - - if (screenOn == mScreenOn) return; - - mScreenOn = screenOn; - long now = SystemClock.elapsedRealtime(); - if (mScreenOn) { - mLastScreenOnEventRealtime = now; - } else { - mScreenOnTime += now - mLastScreenOnEventRealtime; - writeScreenOnTimeLocked(mScreenOnTime); - } - } - - long getScreenOnTimeLocked() { - long screenOnTime = mScreenOnTime; - if (mScreenOn) { - screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime; - } - return screenOnTime; - } - - private File getScreenOnTimeFile() { - return new File(mUsageStatsDir, UserHandle.USER_SYSTEM + "/screen_on_time"); - } - - private long readScreenOnTimeLocked() { - long screenOnTime = 0; - File screenOnTimeFile = getScreenOnTimeFile(); - if (screenOnTimeFile.exists()) { - try { - BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); - screenOnTime = Long.parseLong(reader.readLine()); - reader.close(); - } catch (IOException | NumberFormatException e) { - } - } else { - writeScreenOnTimeLocked(screenOnTime); - } - return screenOnTime; - } - - private void writeScreenOnTimeLocked(long screenOnTime) { - AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); - FileOutputStream fos = null; - try { - fos = screenOnTimeFile.startWrite(); - fos.write(Long.toString(screenOnTime).getBytes()); - screenOnTimeFile.finishWrite(fos); - } catch (IOException ioe) { - screenOnTimeFile.failWrite(fos); - } - } - void onDeviceIdleModeChanged() { final boolean deviceIdle = mPowerManager.isDeviceIdleMode(); if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle); @@ -549,7 +507,7 @@ public class UsageStatsService extends SystemService implements if (service == null) { service = new UserUsageStatsService(getContext(), userId, new File(mUsageStatsDir, Integer.toString(userId)), this); - service.init(currentTimeMillis, getScreenOnTimeLocked()); + service.init(currentTimeMillis); mUserState.put(userId, service); } return service; @@ -569,8 +527,7 @@ public class UsageStatsService extends SystemService implements final int userCount = mUserState.size(); for (int i = 0; i < userCount; i++) { final UserUsageStatsService service = mUserState.valueAt(i); - service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(), - false); + service.onTimeChanged(expectedSystemTime, actualSystemTime); } mRealTimeSnapshot = actualRealtime; mSystemTimeSnapshot = actualSystemTime; @@ -602,26 +559,26 @@ public class UsageStatsService extends SystemService implements void reportEvent(UsageEvents.Event event, int userId) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); - final long screenOnTime = getScreenOnTimeLocked(); + final long elapsedRealtime = SystemClock.elapsedRealtime(); convertToSystemTimeLocked(event); final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - final long beginIdleTime = service.getBeginIdleTime(event.mPackage); - final long lastUsedTime = service.getSystemLastUsedTime(event.mPackage); - final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime, - lastUsedTime, screenOnTime, timeNow); - service.reportEvent(event, screenOnTime); + // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back + // about apps that are on some kind of whitelist anyway. + final boolean previouslyIdle = mAppIdleHistory.isIdleLocked( + event.mPackage, userId, elapsedRealtime); + service.reportEvent(event); // Inform listeners if necessary if ((event.mEventType == Event.MOVE_TO_FOREGROUND || event.mEventType == Event.MOVE_TO_BACKGROUND || event.mEventType == Event.SYSTEM_INTERACTION || event.mEventType == Event.USER_INTERACTION)) { + mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime); if (previouslyIdle) { mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, /* idle = */ 0, event.mPackage)); notifyBatteryStats(event.mPackage, userId, false); - mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow); } } } @@ -655,28 +612,23 @@ public class UsageStatsService extends SystemService implements * the threshold for idle. */ void forceIdleState(String packageName, int userId, boolean idle) { + final int appId = getAppId(packageName); + if (appId < 0) return; synchronized (mLock) { - final long timeNow = checkAndGetTimeLocked(); - final long screenOnTime = getScreenOnTimeLocked(); - final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000; + final long elapsedRealtime = SystemClock.elapsedRealtime(); - final UserUsageStatsService service = - getUserDataAndInitializeIfNeededLocked(userId, timeNow); - final long beginIdleTime = service.getBeginIdleTime(packageName); - final long lastUsedTime = service.getSystemLastUsedTime(packageName); - final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime, - lastUsedTime, screenOnTime, timeNow); - service.setBeginIdleTime(packageName, deviceUsageTime); - service.setSystemLastUsedTime(packageName, - timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000); + final boolean previouslyIdle = isAppIdleFiltered(packageName, appId, + userId, elapsedRealtime); + mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime); + final boolean stillIdle = isAppIdleFiltered(packageName, appId, + userId, elapsedRealtime); // Inform listeners if necessary - if (previouslyIdle != idle) { + if (previouslyIdle != stillIdle) { mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, - /* idle = */ idle ? 1 : 0, packageName)); - if (!idle) { + /* idle = */ stillIdle ? 1 : 0, packageName)); + if (!stillIdle) { notifyBatteryStats(packageName, userId, idle); } - mAppIdleHistory.addEntry(packageName, userId, idle, timeNow); } } } @@ -693,10 +645,11 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - void removeUser(int userId) { + void onUserRemoved(int userId) { synchronized (mLock) { Slog.i(TAG, "Removing user " + userId + " and all data."); mUserState.remove(userId); + mAppIdleHistory.onUserRemoved(userId); cleanUpRemovedUsersLocked(); } } @@ -750,29 +703,12 @@ public class UsageStatsService extends SystemService implements } } - private boolean isAppIdleUnfiltered(String packageName, UserUsageStatsService userService, - long timeNow, long screenOnTime) { + private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) { synchronized (mLock) { - long beginIdleTime = userService.getBeginIdleTime(packageName); - long lastUsedTime = userService.getSystemLastUsedTime(packageName); - return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime, - timeNow); + return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime); } } - /** - * @param beginIdleTime when the app was last used in device usage timebase - * @param lastUsedTime wallclock time of when the app was last used - * @param screenOnTime screen-on timebase time - * @param currentTime current time in device usage timebase - * @return whether it's been used far enough in the past to be considered inactive - */ - boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime, - long screenOnTime, long currentTime) { - return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis) - && (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis); - } - void addListener(AppIdleStateChangeListener listener) { synchronized (mLock) { if (!mPackageAccessListeners.contains(listener)) { @@ -787,32 +723,22 @@ public class UsageStatsService extends SystemService implements } } - boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) { - if (mAppIdleParoled) { - return false; - } + int getAppId(String packageName) { try { ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName, - PackageManager.GET_UNINSTALLED_PACKAGES - | PackageManager.GET_DISABLED_COMPONENTS); - return isAppIdleFiltered(packageName, ai.uid, userId, timeNow); - } catch (PackageManager.NameNotFoundException e) { + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS); + return ai.uid; + } catch (NameNotFoundException re) { + return -1; } - return false; } - boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) { - final UserUsageStatsService userService; - final long screenOnTime; - synchronized (mLock) { - if (timeNow == -1) { - timeNow = checkAndGetTimeLocked(); - } - userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - screenOnTime = getScreenOnTimeLocked(); + boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) { + if (mAppIdleParoled) { + return false; } - return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId, - userService, timeNow, screenOnTime); + return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime); } /** @@ -822,7 +748,7 @@ public class UsageStatsService extends SystemService implements * Called by interface impls. */ private boolean isAppIdleFiltered(String packageName, int appId, int userId, - UserUsageStatsService userService, long timeNow, long screenOnTime) { + long elapsedRealtime) { if (packageName == null) return false; // If not enabled at all, of course nobody is ever idle. if (!mAppIdleEnabled) { @@ -864,7 +790,7 @@ public class UsageStatsService extends SystemService implements return false; } - return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime); + return isAppIdleUnfiltered(packageName, userId, elapsedRealtime); } int[] getIdleUidsForUser(int userId) { @@ -872,14 +798,7 @@ public class UsageStatsService extends SystemService implements return new int[0]; } - final long timeNow; - final UserUsageStatsService userService; - final long screenOnTime; - synchronized (mLock) { - timeNow = checkAndGetTimeLocked(); - userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - screenOnTime = getScreenOnTimeLocked(); - } + final long elapsedRealtime = SystemClock.elapsedRealtime(); List<ApplicationInfo> apps; try { @@ -899,12 +818,12 @@ public class UsageStatsService extends SystemService implements // Now resolve all app state. Iterating over all apps, keeping track of how many // we find for each uid and how many of those are idle. - for (int i = apps.size()-1; i >= 0; i--) { + for (int i = apps.size() - 1; i >= 0; i--) { ApplicationInfo ai = apps.get(i); // Check whether this app is idle. boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), - userId, userService, timeNow, screenOnTime); + userId, elapsedRealtime); int index = uidStates.indexOfKey(ai.uid); if (index < 0) { @@ -990,8 +909,11 @@ public class UsageStatsService extends SystemService implements for (int i = 0; i < userCount; i++) { UserUsageStatsService service = mUserState.valueAt(i); service.persistActiveStats(); + mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i)); } - + // Persist elapsed time periodically, in case screen doesn't get toggled + // until the next boot + mAppIdleHistory.writeElapsedTimeLocked(); mHandler.removeMessages(MSG_FLUSH_TO_DISK); } @@ -1000,7 +922,6 @@ public class UsageStatsService extends SystemService implements */ void dump(String[] args, PrintWriter pw) { synchronized (mLock) { - final long screenOnTime = getScreenOnTimeLocked(); IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); ArraySet<String> argSet = new ArraySet<>(); argSet.addAll(Arrays.asList(args)); @@ -1011,27 +932,28 @@ public class UsageStatsService extends SystemService implements idpw.println(); idpw.increaseIndent(); if (argSet.contains("--checkin")) { - mUserState.valueAt(i).checkin(idpw, screenOnTime); + mUserState.valueAt(i).checkin(idpw); } else { - mUserState.valueAt(i).dump(idpw, screenOnTime); + mUserState.valueAt(i).dump(idpw); idpw.println(); - if (args.length > 0 && "history".equals(args[0])) { - mAppIdleHistory.dump(idpw, mUserState.keyAt(i)); + if (args.length > 0) { + if ("history".equals(args[0])) { + mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i)); + } else if ("flush".equals(args[0])) { + UsageStatsService.this.flushToDiskLocked(); + pw.println("Flushed stats to disk"); + } } } + mAppIdleHistory.dump(idpw, mUserState.keyAt(i)); idpw.decreaseIndent(); } - pw.print("Screen On Timebase: "); - pw.print(screenOnTime); - pw.print(" ("); - TimeUtils.formatDuration(screenOnTime, pw); - pw.println(")"); pw.println(); pw.println("Settings:"); pw.print(" mAppIdleDurationMillis="); - TimeUtils.formatDuration(mAppIdleDurationMillis, pw); + TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw); pw.println(); pw.print(" mAppIdleWallclockThresholdMillis="); @@ -1057,11 +979,6 @@ public class UsageStatsService extends SystemService implements pw.print("mLastAppIdleParoledTime="); TimeUtils.formatDuration(mLastAppIdleParoledTime, pw); pw.println(); - pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw); - pw.println(); - pw.print("mLastScreenOnEventRealtime="); - TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw); - pw.println(); } } @@ -1082,7 +999,7 @@ public class UsageStatsService extends SystemService implements break; case MSG_REMOVE_USER: - removeUser(msg.arg1); + onUserRemoved(msg.arg1); break; case MSG_INFORM_LISTENERS: @@ -1179,13 +1096,13 @@ public class UsageStatsService extends SystemService implements } // Default: 12 hours of screen-on time sans dream-time - mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION, + mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION, COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE); mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD, COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days - mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4, + mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4, COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours // Default: 24 hours between paroles @@ -1194,6 +1111,8 @@ public class UsageStatsService extends SystemService implements mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION, COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes + mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis, + mAppIdleScreenThresholdMillis); } } } @@ -1284,7 +1203,8 @@ public class UsageStatsService extends SystemService implements } final long token = Binder.clearCallingIdentity(); try { - return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1); + return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, + SystemClock.elapsedRealtime()); } finally { Binder.restoreCallingIdentity(token); } @@ -1304,11 +1224,9 @@ public class UsageStatsService extends SystemService implements "No permission to change app idle state"); final long token = Binder.clearCallingIdentity(); try { - PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName, - PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); - if (pi == null) return; + final int appId = getAppId(packageName); + if (appId < 0) return; UsageStatsService.this.setAppIdle(packageName, idle, userId); - } catch (RemoteException re) { } finally { Binder.restoreCallingIdentity(token); } @@ -1335,8 +1253,6 @@ public class UsageStatsService extends SystemService implements } UsageStatsService.this.dump(args, pw); } - - } /** @@ -1411,7 +1327,8 @@ public class UsageStatsService extends SystemService implements @Override public boolean isAppIdle(String packageName, int uidForAppId, int userId) { - return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1); + return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, + SystemClock.elapsedRealtime()); } @Override diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index f2ca3a4047fa..c95ff2364f2c 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -26,7 +26,6 @@ import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; -import android.text.TextUtils; import java.io.IOException; import java.net.ProtocolException; @@ -55,13 +54,11 @@ final class UsageStatsXmlV1 { // Time attributes stored as an offset of the beginTime. private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive"; - private static final String LAST_TIME_ACTIVE_SYSTEM_ATTR = "lastTimeActiveSystem"; - private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime"; private static final String END_TIME_ATTR = "endTime"; private static final String TIME_ATTR = "time"; private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut) - throws XmlPullParserException, IOException { + throws IOException { final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR); if (pkg == null) { throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present"); @@ -72,20 +69,6 @@ final class UsageStatsXmlV1 { // Apply the offset to the beginTime to find the absolute time. stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_ACTIVE_ATTR); - - final String lastTimeUsedSystem = parser.getAttributeValue(null, - LAST_TIME_ACTIVE_SYSTEM_ATTR); - if (TextUtils.isEmpty(lastTimeUsedSystem)) { - // If the field isn't present, use the old one. - stats.mLastTimeSystemUsed = stats.mLastTimeUsed; - } else { - stats.mLastTimeSystemUsed = statsOut.beginTime + Long.parseLong(lastTimeUsedSystem); - } - - final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR); - if (!TextUtils.isEmpty(beginIdleTime)) { - stats.mBeginIdleTime = Long.parseLong(beginIdleTime); - } stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); } @@ -141,13 +124,10 @@ final class UsageStatsXmlV1 { // Write the time offset. XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, usageStats.mLastTimeUsed - stats.beginTime); - XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_SYSTEM_ATTR, - usageStats.mLastTimeSystemUsed - stats.beginTime); XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); - XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime); xml.endTag(null, PACKAGE_TAG); } @@ -255,7 +235,6 @@ final class UsageStatsXmlV1 { } xml.endTag(null, PACKAGES_TAG); - xml.startTag(null, CONFIGURATIONS_TAG); final int configCount = stats.configurations.size(); for (int i = 0; i < configCount; i++) { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index f2045d31f69c..7d003f3bf61a 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -59,7 +59,6 @@ class UserUsageStatsService { private final Context mContext; private final UsageStatsDatabase mDatabase; private final IntervalStats[] mCurrentStats; - private IntervalStats mAppIdleRollingWindow; private boolean mStatsChanged = false; private final UnixCalendar mDailyExpiryDate; private final StatsUpdatedListener mListener; @@ -74,7 +73,11 @@ class UserUsageStatsService { interface StatsUpdatedListener { void onStatsUpdated(); void onStatsReloaded(); - long getAppIdleRollingWindowDurationMillis(); + /** + * Callback that a system update was detected + * @param mUserId user that needs to be initialized + */ + void onNewUpdate(int mUserId); } UserUsageStatsService(Context context, int userId, File usageStatsDir, @@ -88,7 +91,7 @@ class UserUsageStatsService { mUserId = userId; } - void init(final long currentTimeMillis, final long deviceUsageTime) { + void init(final long currentTimeMillis) { mDatabase.init(currentTimeMillis); int nullCount = 0; @@ -112,7 +115,7 @@ class UserUsageStatsService { // By calling loadActiveStats, we will // generate new stats for each bucket. - loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false); + loadActiveStats(currentTimeMillis); } else { // Set up the expiry date to be one day from the latest daily stat. // This may actually be today and we will rollover on the first event @@ -136,54 +139,18 @@ class UserUsageStatsService { stat.updateConfigurationStats(null, stat.lastTimeSaved); } - refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime); - if (mDatabase.isNewUpdate()) { - initializeDefaultsForApps(currentTimeMillis, deviceUsageTime, - mDatabase.isFirstUpdate()); + notifyNewUpdate(); } } - /** - * If any of the apps don't have a last-used entry, add one now. - * @param currentTimeMillis the current time - * @param firstUpdate if it is the first update, touch all installed apps, otherwise only - * touch the system apps - */ - private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime, - boolean firstUpdate) { - PackageManager pm = mContext.getPackageManager(); - List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId); - final int packageCount = packages.size(); - for (int i = 0; i < packageCount; i++) { - final PackageInfo pi = packages.get(i); - String packageName = pi.packageName; - if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp()) - && getBeginIdleTime(packageName) == -1) { - for (IntervalStats stats : mCurrentStats) { - stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION); - stats.updateBeginIdleTime(packageName, deviceUsageTime); - } - - mAppIdleRollingWindow.update(packageName, currentTimeMillis, - Event.SYSTEM_INTERACTION); - mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime); - mStatsChanged = true; - } - } - // Persist the new OTA-related access stats. - persistActiveStats(); - } - - void onTimeChanged(long oldTime, long newTime, long deviceUsageTime, - boolean resetBeginIdleTime) { + void onTimeChanged(long oldTime, long newTime) { persistActiveStats(); mDatabase.onTimeChanged(newTime - oldTime); - loadActiveStats(newTime, resetBeginIdleTime); - refreshAppIdleRollingWindow(newTime, deviceUsageTime); + loadActiveStats(newTime); } - void reportEvent(UsageEvents.Event event, long deviceUsageTime) { + void reportEvent(UsageEvents.Event event) { if (DEBUG) { Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage + "[" + event.mTimeStamp + "]: " @@ -192,7 +159,7 @@ class UserUsageStatsService { if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) { // Need to rollover - rolloverStats(event.mTimeStamp, deviceUsageTime); + rolloverStats(event.mTimeStamp); } final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY]; @@ -218,35 +185,9 @@ class UserUsageStatsService { stats.updateConfigurationStats(newFullConfig, event.mTimeStamp); } else { stats.update(event.mPackage, event.mTimeStamp, event.mEventType); - stats.updateBeginIdleTime(event.mPackage, deviceUsageTime); } } - if (event.mEventType != Event.CONFIGURATION_CHANGE) { - mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType); - mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime); - } - - notifyStatsChanged(); - } - - /** - * Sets the beginIdleTime for each of the intervals. - * @param beginIdleTime - */ - void setBeginIdleTime(String packageName, long beginIdleTime) { - for (IntervalStats stats : mCurrentStats) { - stats.updateBeginIdleTime(packageName, beginIdleTime); - } - mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime); - notifyStatsChanged(); - } - - void setSystemLastUsedTime(String packageName, long lastUsedTime) { - for (IntervalStats stats : mCurrentStats) { - stats.updateSystemLastUsedTime(packageName, lastUsedTime); - } - mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime); notifyStatsChanged(); } @@ -404,24 +345,6 @@ class UserUsageStatsService { return new UsageEvents(results, table); } - long getBeginIdleTime(String packageName) { - UsageStats packageUsage; - if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) { - return -1; - } else { - return packageUsage.getBeginIdleTime(); - } - } - - long getSystemLastUsedTime(String packageName) { - UsageStats packageUsage; - if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) { - return -1; - } else { - return packageUsage.getLastTimeSystemUsed(); - } - } - void persistActiveStats() { if (mStatsChanged) { Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk"); @@ -436,7 +359,7 @@ class UserUsageStatsService { } } - private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) { + private void rolloverStats(final long currentTimeMillis) { final long startTime = SystemClock.elapsedRealtime(); Slog.i(TAG, mLogPrefix + "Rolling over usage stats"); @@ -463,7 +386,7 @@ class UserUsageStatsService { persistActiveStats(); mDatabase.prune(currentTimeMillis); - loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false); + loadActiveStats(currentTimeMillis); final int continueCount = continuePreviousDay.size(); for (int i = 0; i < continueCount; i++) { @@ -477,8 +400,6 @@ class UserUsageStatsService { } persistActiveStats(); - refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime); - final long totalTime = SystemClock.elapsedRealtime() - startTime; Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime + " milliseconds"); @@ -491,7 +412,11 @@ class UserUsageStatsService { } } - private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) { + private void notifyNewUpdate() { + mListener.onNewUpdate(mUserId); + } + + private void loadActiveStats(final long currentTimeMillis) { for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) { final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType); if (stats != null && currentTimeMillis - 500 >= stats.endTime && @@ -514,12 +439,6 @@ class UserUsageStatsService { mCurrentStats[intervalType].beginTime = currentTimeMillis; mCurrentStats[intervalType].endTime = currentTimeMillis + 1; } - - if (resetBeginIdleTime) { - for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) { - usageStats.mBeginIdleTime = 0; - } - } } mStatsChanged = false; @@ -538,96 +457,28 @@ class UserUsageStatsService { mDailyExpiryDate.getTimeInMillis() + ")"); } - private static void mergePackageStats(IntervalStats dst, IntervalStats src, - final long deviceUsageTime) { - dst.endTime = Math.max(dst.endTime, src.endTime); - - final int srcPackageCount = src.packageStats.size(); - for (int i = 0; i < srcPackageCount; i++) { - final String packageName = src.packageStats.keyAt(i); - final UsageStats srcStats = src.packageStats.valueAt(i); - UsageStats dstStats = dst.packageStats.get(packageName); - if (dstStats == null) { - dstStats = new UsageStats(srcStats); - dst.packageStats.put(packageName, dstStats); - } else { - dstStats.add(src.packageStats.valueAt(i)); - } - - // App idle times can not begin in the future. This happens if we had a time change. - if (dstStats.mBeginIdleTime > deviceUsageTime) { - dstStats.mBeginIdleTime = deviceUsageTime; - } - } - } - - /** - * App idle operates on a rolling window of time. When we roll over time, we end up with a - * period of time where in-memory stats are empty and we don't hit the disk for older stats - * for performance reasons. Suddenly all apps will become idle. - * - * Instead, at times we do a deep query to find all the apps that have run in the past few - * days and keep the cached data up to date. - * - * @param currentTimeMillis - */ - void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) { - // Start the rolling window for AppIdle requests. - final long startRangeMillis = currentTimeMillis - - mListener.getAppIdleRollingWindowDurationMillis(); - - List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, - startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() { - @Override - public void combine(IntervalStats stats, boolean mutable, - List<IntervalStats> accumulatedResult) { - IntervalStats accum; - if (accumulatedResult.isEmpty()) { - accum = new IntervalStats(); - accum.beginTime = stats.beginTime; - accumulatedResult.add(accum); - } else { - accum = accumulatedResult.get(0); - } - - mergePackageStats(accum, stats, deviceUsageTime); - } - }); - - if (stats == null || stats.isEmpty()) { - mAppIdleRollingWindow = new IntervalStats(); - mergePackageStats(mAppIdleRollingWindow, - mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime); - } else { - mAppIdleRollingWindow = stats.get(0); - } - } - // // -- DUMP related methods -- // - void checkin(final IndentingPrintWriter pw, final long screenOnTime) { + void checkin(final IndentingPrintWriter pw) { mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() { @Override public boolean checkin(IntervalStats stats) { - printIntervalStats(pw, stats, screenOnTime, false); + printIntervalStats(pw, stats, false); return true; } }); } - void dump(IndentingPrintWriter pw, final long screenOnTime) { + void dump(IndentingPrintWriter pw) { // This is not a check-in, only dump in-memory stats. for (int interval = 0; interval < mCurrentStats.length; interval++) { pw.print("In-memory "); pw.print(intervalToString(interval)); pw.println(" stats"); - printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true); + printIntervalStats(pw, mCurrentStats[interval], true); } - - pw.println("AppIdleRollingWindow cache"); - printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true); } private String formatDateTime(long dateTime, boolean pretty) { @@ -644,7 +495,7 @@ class UserUsageStatsService { return Long.toString(elapsedTime); } - void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime, + void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) { if (prettyDates) { pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext, @@ -665,10 +516,6 @@ class UserUsageStatsService { pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates)); pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates)); - pw.printPair("lastTimeSystem", - formatDateTime(usageStats.mLastTimeSystemUsed, prettyDates)); - pw.printPair("inactiveTime", - formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates)); pw.println(); } pw.decreaseIndent(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 4368b81b7b06..3ad7d34f6ff1 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -591,6 +591,14 @@ public class CarrierConfigManager { @SystemApi public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; + /** + * The duration in seconds that platform call and message blocking is disabled after the user + * contacts emergency services. Platform considers values in the range 0 to 604800 (one week) as + * valid. See {@link android.provider.BlockedNumberContract#isBlocked(Context, String)}). + */ + public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = + "duration_blocking_disabled_after_emergency_int"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -660,6 +668,7 @@ public class CarrierConfigManager { "max_retries=3, 5000, 5000, 5000"); sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000); sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000); + sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200); sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index d1d6e0dcdece..6229ed921bcc 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -25,6 +25,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Typeface; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; @@ -338,7 +339,7 @@ public class SubscriptionInfo implements Parcelable { public static String givePrintableIccid(String iccId) { String iccIdToPrint = null; if (iccId != null) { - if (iccId.length() > 9) { + if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) { iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX"; } else { iccIdToPrint = iccId; diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.java b/telephony/java/com/android/ims/ImsCallForwardInfo.java index 3f8fd19a16ce..eeee0fc938bc 100644 --- a/telephony/java/com/android/ims/ImsCallForwardInfo.java +++ b/telephony/java/com/android/ims/ImsCallForwardInfo.java @@ -31,6 +31,8 @@ public class ImsCallForwardInfo implements Parcelable { public int mStatus; // 0x91: International, 0x81: Unknown public int mToA; + // Service class + public int mServiceClass; // Number (it will not include the "sip" or "tel" URI scheme) public String mNumber; // No reply timer for CF @@ -55,13 +57,16 @@ public class ImsCallForwardInfo implements Parcelable { out.writeInt(mToA); out.writeString(mNumber); out.writeInt(mTimeSeconds); + out.writeInt(mServiceClass); } @Override public String toString() { return super.toString() + ", Condition: " + mCondition + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled") - + ", ToA: " + mToA + ", Number=" + mNumber + + ", ToA: " + mToA + + ", Service Class: " + mServiceClass + + ", Number=" + mNumber + ", Time (seconds): " + mTimeSeconds; } @@ -71,6 +76,7 @@ public class ImsCallForwardInfo implements Parcelable { mToA = in.readInt(); mNumber = in.readString(); mTimeSeconds = in.readInt(); + mServiceClass = in.readInt(); } public static final Creator<ImsCallForwardInfo> CREATOR = diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java index 2769a2b27968..c909c6dc518e 100644 --- a/telephony/java/com/android/ims/ImsReasonInfo.java +++ b/telephony/java/com/android/ims/ImsReasonInfo.java @@ -84,6 +84,8 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // IMS call is already terminated (in TERMINATED state) public static final int CODE_LOCAL_CALL_TERMINATED = 148; + // Handover not feasible + public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; /** * TIMEOUT (IMS -> Telephony) @@ -153,6 +155,9 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_SIP_USER_REJECTED = 361; // Others public static final int CODE_SIP_GLOBAL_ERROR = 362; + // Emergency failure + public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; + public static final int CODE_EMERGENCY_PERM_FAILURE = 364; /** * MEDIA (IMS -> Telephony) @@ -236,6 +241,14 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_ANSWERED_ELSEWHERE = 1014; /** + * Supplementary services (HOLD/RESUME) failure error codes. + * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision. + */ + public static final int CODE_SUPP_SVC_FAILED = 1201; + public static final int CODE_SUPP_SVC_CANCELLED = 1202; + public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; + + /** * Network string error messages. * mExtraMessage may have these values. */ diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.java b/telephony/java/com/android/ims/ImsStreamMediaProfile.java index 5a99212ae6f4..216cef59f301 100644 --- a/telephony/java/com/android/ims/ImsStreamMediaProfile.java +++ b/telephony/java/com/android/ims/ImsStreamMediaProfile.java @@ -51,6 +51,16 @@ public class ImsStreamMediaProfile implements Parcelable { public static final int AUDIO_QUALITY_GSM_EFR = 8; public static final int AUDIO_QUALITY_GSM_FR = 9; public static final int AUDIO_QUALITY_GSM_HR = 10; + public static final int AUDIO_QUALITY_G711U = 11; + public static final int AUDIO_QUALITY_G723 = 12; + public static final int AUDIO_QUALITY_G711A = 13; + public static final int AUDIO_QUALITY_G722 = 14; + public static final int AUDIO_QUALITY_G711AB = 15; + public static final int AUDIO_QUALITY_G729 = 16; + public static final int AUDIO_QUALITY_EVS_NB = 17; + public static final int AUDIO_QUALITY_EVS_WB = 18; + public static final int AUDIO_QUALITY_EVS_SWB = 19; + public static final int AUDIO_QUALITY_EVS_FB = 20; /** * Video information diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index b028ce61f821..de7b9c268c99 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -356,6 +356,15 @@ </activity> <activity + android:name="MovingSurfaceViewActivity" + android:label="SurfaceView/Animated Movement"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity android:name="GLTextureViewActivity" android:label="TextureView/OpenGL"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java new file mode 100644 index 000000000000..cd15ef156a5c --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; + +public class MovingSurfaceViewActivity extends Activity implements Callback { + static final String TAG = "MovingSurfaceView"; + SurfaceView mSurfaceView; + ObjectAnimator mAnimator; + + class MySurfaceView extends SurfaceView { + boolean mSlowToggled; + + public MySurfaceView(Context context) { + super(context); + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mSlowToggled = !mSlowToggled; + Log.d(TAG, "SLOW MODE: " + mSlowToggled); + invalidate(); + } + }); + setWillNotDraw(false); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (mSlowToggled) { + try { + Thread.sleep(16); + } catch (InterruptedException e) {} + } + } + + public void setMyTranslationY(float ty) { + setTranslationY(ty); + if (mSlowToggled) { + invalidate(); + } + } + + public float getMyTranslationY() { + return getTranslationY(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this); + + mSurfaceView = new MySurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + + final float density = getResources().getDisplayMetrics().density; + int size = (int) (200 * density); + + content.addView(mSurfaceView, new FrameLayout.LayoutParams( + size, size, Gravity.CENTER)); + mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY", + 0, size); + mAnimator.setRepeatMode(ObjectAnimator.REVERSE); + mAnimator.setRepeatCount(ObjectAnimator.INFINITE); + mAnimator.setDuration(200); + mAnimator.setInterpolator(new LinearInterpolator()); + setContentView(content); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Canvas canvas = holder.lockCanvas(); + canvas.drawARGB(0xFF, 0x00, 0xFF, 0x00); + holder.unlockCanvasAndPost(canvas); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + @Override + protected void onResume() { + super.onResume(); + mAnimator.start(); + } + + @Override + protected void onPause() { + mAnimator.pause(); + super.onPause(); + } +} diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl index 13efc361fbb3..fa666afd1c27 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -26,7 +26,7 @@ import android.net.wifi.nan.ConfigRequest; oneway interface IWifiNanEventListener { void onConfigCompleted(in ConfigRequest completedConfig); - void onConfigFailed(int reason); + void onConfigFailed(in ConfigRequest failedConfig, int reason); void onNanDown(int reason); void onIdentityChanged(); } diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl index ec9e4628d609..f382d97762d3 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl index 50c34d946918..d60d8caae70e 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java index eae0a55f7af1..5c18bd7e0f07 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java @@ -47,7 +47,8 @@ public class WifiNanEventListener { /** * Configuration failed callback event registration flag. Corresponding - * callback is {@link WifiNanEventListener#onConfigFailed(int)}. + * callback is + * {@link WifiNanEventListener#onConfigFailed(ConfigRequest, int)}. */ public static final int LISTEN_CONFIG_FAILED = 0x1 << 1; @@ -93,7 +94,7 @@ public class WifiNanEventListener { WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj); break; case LISTEN_CONFIG_FAILED: - WifiNanEventListener.this.onConfigFailed(msg.arg1); + WifiNanEventListener.this.onConfigFailed((ConfigRequest) msg.obj, msg.arg1); break; case LISTEN_NAN_DOWN: WifiNanEventListener.this.onNanDown(msg.arg1); @@ -129,7 +130,7 @@ public class WifiNanEventListener { * * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}. */ - public void onConfigFailed(int reason) { + public void onConfigFailed(ConfigRequest failedConfig, int reason) { Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable"); } @@ -173,11 +174,14 @@ public class WifiNanEventListener { } @Override - public void onConfigFailed(int reason) { - if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason); + public void onConfigFailed(ConfigRequest failedConfig, int reason) { + if (VDBG) { + Log.v(TAG, "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason); + } Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED); msg.arg1 = reason; + msg.obj = failedConfig; mHandler.sendMessage(msg); } diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java index d5e59f0edaf5..092508766570 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java @@ -303,8 +303,8 @@ public class WifiNanSessionListener { * message). Override to implement your custom response. * <p> * Note that either this callback or - * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received - - * never both. + * {@link WifiNanSessionListener#onMessageSendFail(int, int)} will be + * received - never both. */ public void onMessageSendSuccess(int messageId) { if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested"); @@ -319,8 +319,8 @@ public class WifiNanSessionListener { * message). Override to implement your custom response. * <p> * Note that either this callback or - * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received - - * never both + * {@link WifiNanSessionListener#onMessageSendSuccess(int)} will be received + * - never both * * @param reason The failure reason using {@code NanSessionListener.FAIL_*} * codes. |