diff options
34 files changed, 863 insertions, 384 deletions
diff --git a/api/current.txt b/api/current.txt index cb63af7..7e24003 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5712,6 +5712,7 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; + field public static final java.lang.String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN"; @@ -24148,8 +24149,16 @@ package android.provider { field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM"; field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER"; field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS"; + field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM"; + field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM"; + field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all"; + field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next"; + field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none"; + field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time"; + field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE"; field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS"; field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR"; + field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM"; field public static final java.lang.String EXTRA_LENGTH = "android.intent.extra.alarm.LENGTH"; field public static final java.lang.String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE"; field public static final java.lang.String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES"; diff --git a/api/system-current.txt b/api/system-current.txt index 7ef0008..3f2e19b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5818,6 +5818,7 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; + field public static final java.lang.String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN"; @@ -26031,8 +26032,16 @@ package android.provider { field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM"; field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER"; field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS"; + field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM"; + field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM"; + field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all"; + field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next"; + field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none"; + field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time"; + field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE"; field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS"; field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR"; + field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM"; field public static final java.lang.String EXTRA_LENGTH = "android.intent.extra.alarm.LENGTH"; field public static final java.lang.String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE"; field public static final java.lang.String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e917522..68f4707 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -502,6 +502,20 @@ public class DevicePolicyManager { */ public static final String EXTRA_PROVISIONING_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY"; + + /** + * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that + * holds data needed by the system to wipe factory reset protection. The data needed to wipe + * the device depend on the installed factory reset protection implementation. For example, + * if an account is needed to unlock a device, this extra may contain data used to + * authenticate that account. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS + = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS"; + /** * This MIME type is used for starting the Device Owner provisioning that does not require * provisioning features introduced in Android API level diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index ca6dc69..1131ff9 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -464,7 +464,7 @@ public class BackupTransport { * transport level). * * <p>After this method returns zero, the system will then call - * {@link #getNextFullRestorePackage()} to begin the restore process for the next + * {@link #nextRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * * <p>The transport should always close this socket when returning from this method. diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java index fd69d20..1a64da6 100644 --- a/core/java/android/app/backup/RecentsBackupHelper.java +++ b/core/java/android/app/backup/RecentsBackupHelper.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.app.backup; import android.content.Context; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fb7c96d..649bb47 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -269,6 +269,12 @@ interface IPackageManager { void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); /** + * Backup/restore support - only the system uid may use these. + */ + byte[] getPreferredActivityBackup(int userId); + void restorePreferredActivities(in byte[] backup, int userId); + + /** * Report the set of 'Home' activity candidates, plus (if any) which of them * is the current "always use this one" setting. */ diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 299eb7e..334d180 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -117,6 +117,9 @@ public class Resources { private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>> sPreloadedColorStateLists = new LongSparseArray<>(); + private static final String CACHE_NOT_THEMED = ""; + private static final String CACHE_NULL_THEME = "null_theme"; + // Pool of TypedArrays targeted to this Resources object. final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5); @@ -2441,8 +2444,8 @@ public class Resources { } } - // Next, check preloaded drawables. These are unthemed but may have - // themeable attributes. + // Next, check preloaded drawables. These may contain unresolved theme + // attributes. final ConstantState cs; if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); @@ -2450,42 +2453,49 @@ public class Resources { cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } - final Drawable dr; + Drawable dr; if (cs != null) { - final Drawable clonedDr = cs.newDrawable(this); - if (theme != null) { - dr = clonedDr.mutate(); - dr.applyTheme(theme); - dr.clearMutated(); - } else { - dr = clonedDr; - } + dr = cs.newDrawable(this); } else if (isColorDrawable) { dr = new ColorDrawable(value.data); } else { - dr = loadDrawableForCookie(value, id, theme); + dr = loadDrawableForCookie(value, id, null); + } + + // Determine if the drawable has unresolved theme attributes. If it + // does, we'll need to apply a theme and store it in a theme-specific + // cache. + final String cacheKey; + if (!dr.canApplyTheme()) { + cacheKey = CACHE_NOT_THEMED; + } else if (theme == null) { + cacheKey = CACHE_NULL_THEME; + } else { + cacheKey = theme.getKey(); + dr = dr.mutate(); + dr.applyTheme(theme); + dr.clearMutated(); } // If we were able to obtain a drawable, store it in the appropriate - // cache (either preload or themed). + // cache: preload, not themed, null theme, or theme-specific. if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); - cacheDrawable(value, theme, isColorDrawable, caches, key, dr); + cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr); } return dr; } - private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable, + private void cacheDrawable(TypedValue value, boolean isColorDrawable, ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - long key, Drawable dr) { + String cacheKey, long key, Drawable dr) { final ConstantState cs = dr.getConstantState(); if (cs == null) { return; } if (mPreloading) { - // Preloaded drawables never have a theme, but may be themeable. final int changingConfigs = cs.getChangingConfigurations(); if (isColorDrawable) { if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) { @@ -2507,14 +2517,13 @@ public class Resources { } } else { synchronized (mAccessLock) { - final String themeKey = theme == null ? "" : theme.mKey; - LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey); + LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey); if (themedCache == null) { // Clean out the caches before we add more. This shouldn't // happen very often. pruneCaches(caches); themedCache = new LongSparseArray<>(1); - caches.put(themeKey, themedCache); + caches.put(cacheKey, themedCache); } themedCache.put(key, new WeakReference<>(cs)); } @@ -2612,46 +2621,47 @@ public class Resources { ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, long key, Theme theme) { synchronized (mAccessLock) { - final String themeKey = theme != null ? theme.mKey : ""; - final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey); - if (themedCache != null) { - final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key); - if (themedDrawable != null) { - return themedDrawable; - } + // First search theme-agnostic cache. + final Drawable unthemedDrawable = getCachedDrawableLocked( + caches, key, CACHE_NOT_THEMED); + if (unthemedDrawable != null) { + return unthemedDrawable; } - // No cached drawable, we'll need to create a new one. - return null; + // Next search theme-specific cache. + final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME; + return getCachedDrawableLocked(caches, key, themeKey); } } + private Drawable getCachedDrawableLocked( + ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, + long key, String themeKey) { + final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey); + if (cache != null) { + final ConstantState entry = getConstantStateLocked(cache, key); + if (entry != null) { + return entry.newDrawable(this); + } + } + return null; + } + private ConstantState getConstantStateLocked( LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { final WeakReference<ConstantState> wr = drawableCache.get(key); - if (wr != null) { // we have the key + if (wr != null) { final ConstantState entry = wr.get(); if (entry != null) { - //Log.i(TAG, "Returning cached drawable @ #" + - // Integer.toHexString(((Integer)key).intValue()) - // + " in " + this + ": " + entry); return entry; - } else { // our entry has been purged + } else { + // Our entry has been purged. drawableCache.delete(key); } } return null; } - private Drawable getCachedDrawableLocked( - LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { - final ConstantState entry = getConstantStateLocked(drawableCache, key); - if (entry != null) { - return entry.newDrawable(this); - } - return null; - } - @Nullable ColorStateList loadColorStateList(TypedValue value, int id, Theme theme) throws NotFoundException { diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java index 724d76d..25a35e1 100644 --- a/core/java/android/provider/AlarmClock.java +++ b/core/java/android/provider/AlarmClock.java @@ -43,8 +43,14 @@ public final class AlarmClock { * should remove this alarm after it has been dismissed. If an identical alarm exists matching * all parameters, the implementation may re-use it instead of creating a new one (in this case, * the alarm should not be removed after dismissal). - * + * </p><p> * This action always enables the alarm. + * </p><p> + * This activity could also be started in Voice Interaction mode. The activity should check + * {@link android.app.Activity#isVoiceInteraction}, and if true, the implementation should + * report a deeplink of the created/enabled alarm using + * {@link android.app.VoiceInteractor.CompleteVoiceRequest}. This allows follow-on voice actions + * such as {@link #ACTION_VOICE_CANCEL_ALARM} to cancel the alarm that was just enabled. * </p> * <h3>Request parameters</h3> * <ul> @@ -63,6 +69,48 @@ public final class AlarmClock { public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM"; /** + * Voice Activity Action: Cancel an alarm. + * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be + * started in Voice Interaction mode. + * <p> + * Cancels the specified alarm by voice. To cancel means to disable, but not delete, the alarm. + * See {@link #ACTION_VOICE_DELETE_ALARM} to delete an alarm by voice. + * </p><p> + * The alarm to cancel can be specified or searched for in one of the following ways: + * <ol> + * <li>The Intent's data URI specifies a deeplink to the alarm. + * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is + * required to determine how to search for the alarm. + * + * @see #ACTION_VOICE_DELETE_ALARM + * @see #EXTRA_ALARM_SEARCH_MODE + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VOICE_CANCEL_ALARM = + "android.intent.action.VOICE_CANCEL_ALARM"; + + /** + * Voice Activity Action: Delete an alarm. + * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be + * started in Voice Interaction mode. + * <p> + * Deletes the specified alarm by voice. + * See {@link #ACTION_VOICE_CANCEL_ALARM} to cancel (disable) an alarm by voice. + * </p><p> + * The alarm to delete can be specified or searched for in one of the following ways: + * <ol> + * <li>The Intent's data URI specifies a deeplink to the alarm. + * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is + * required to determine how to search for the alarm. + * + * @see #ACTION_VOICE_CANCEL_ALARM + * @see #EXTRA_ALARM_SEARCH_MODE + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VOICE_DELETE_ALARM = + "android.intent.action.VOICE_DELETE_ALARM"; + + /** * Activity Action: Set a timer. * <p> * Activates an existing timer or creates a new one. @@ -99,6 +147,100 @@ public final class AlarmClock { public static final String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS"; /** + * Bundle extra: Specify the type of search mode to look up an alarm. + * <p> + * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM} to identify + * the alarm(s) to cancel or delete, respectively. + * </p><p> + * This extra is only required when the alarm is not already identified by a deeplink as + * specified in the Intent's data URI. + * </p><p> + * The value of this extra is a {@link String}, restricted to the following set of supported + * search modes: + * <ul> + * <li><i>Time</i> - {@link #ALARM_SEARCH_MODE_TIME}: Selects the alarm that is most + * closely matched by the search parameters {@link #EXTRA_HOUR}, {@link #EXTRA_MINUTES}, + * {@link #EXTRA_IS_PM}. + * <li><i>Next alarm</i> - {@link #ALARM_SEARCH_MODE_NEXT}: Selects the alarm that will + * ring next, or the alarm that is currently ringing, if any. + * <li><i>All alarms</i> - {@link #ALARM_SEARCH_MODE_ALL}: Selects all alarms. + * <li><i>None</i> - {@link #ALARM_SEARCH_MODE_NONE}: No search mode specified. The + * implementation should ask the user to select a search mode using + * {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to + * identify the alarm. + * </ul> + * </ol> + * + * @see #ALARM_SEARCH_MODE_TIME + * @see #ALARM_SEARCH_MODE_NEXT + * @see #ALARM_SEARCH_MODE_ALL + * @see #ALARM_SEARCH_MODE_NONE + * @see #ACTION_VOICE_CANCEL_ALARM + * @see #ACTION_VOICE_DELETE_ALARM + */ + public static final String EXTRA_ALARM_SEARCH_MODE = + "android.intent.extra.alarm.ALARM_SEARCH_MODE"; + + /** + * Search for the alarm that is most closely matched by the search parameters + * {@link #EXTRA_HOUR}, {@link #EXTRA_MINUTES}, {@link #EXTRA_IS_PM}. + * In this search mode, at least one of these additional extras are required. + * <ul> + * <li>{@link #EXTRA_HOUR} - The hour to search for the alarm. + * <li>{@link #EXTRA_MINUTES} - The minute to search for the alarm. + * <li>{@link #EXTRA_IS_PM} - Whether the hour is AM or PM. + * </ul> + * + * @see #EXTRA_ALARM_SEARCH_MODE + */ + public static final String ALARM_SEARCH_MODE_TIME = "time"; + + /** + * Selects the alarm that will ring next, or the alarm that is currently ringing, if any. + * + * @see #EXTRA_ALARM_SEARCH_MODE + */ + public static final String ALARM_SEARCH_MODE_NEXT = "next"; + + /** + * Selects all alarms. + * + * @see #EXTRA_ALARM_SEARCH_MODE + */ + public static final String ALARM_SEARCH_MODE_ALL = "all"; + + /** + * No search mode specified. The implementation should ask the user to select a search mode + * using {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to + * identify the alarm. + * + * @see #EXTRA_ALARM_SEARCH_MODE + */ + public static final String ALARM_SEARCH_MODE_NONE = "none"; + + /** + * Bundle extra: The AM/PM of the alarm. + * <p> + * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM}. + * </p><p> + * This extra is optional and only used when {@link #EXTRA_ALARM_SEARCH_MODE} is set to + * {@link #ALARM_SEARCH_MODE_TIME}. In this search mode, the {@link #EXTRA_IS_PM} is + * used together with {@link #EXTRA_HOUR} and {@link #EXTRA_MINUTES}. The implementation should + * look up the alarm that is most closely matched by these search parameters. + * If {@link #EXTRA_IS_PM} is missing, then the AM/PM of the specified {@link #EXTRA_HOUR} is + * ambiguous and the implementation should ask for clarification from the user. + * </p><p> + * The value is a {@link Boolean}, where false=AM and true=PM. + * </p> + * + * @see #ACTION_VOICE_CANCEL_ALARM + * @see #ACTION_VOICE_DELETE_ALARM + * @see #EXTRA_HOUR + * @see #EXTRA_MINUTES + */ + public static final String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM"; + + /** * Bundle extra: Weekdays for repeating alarm. * <p> * Used by {@link #ACTION_SET_ALARM}. diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 50d701a..16353e8 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -316,7 +316,7 @@ public class ProgressBar extends View { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressTintMode = Drawable.parseTintMode(a.getInt( - R.styleable.ProgressBar_progressBackgroundTintMode, -1), null); + R.styleable.ProgressBar_progressTintMode, -1), null); mProgressTintInfo.mHasProgressTintMode = true; } @@ -334,7 +334,7 @@ public class ProgressBar extends View { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt( - R.styleable.ProgressBar_progressTintMode, -1), null); + R.styleable.ProgressBar_progressBackgroundTintMode, -1), null); mProgressTintInfo.mHasProgressBackgroundTintMode = true; } @@ -365,7 +365,7 @@ public class ProgressBar extends View { mProgressTintInfo.mHasSecondaryProgressTint = true; } - if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) { + if (a.hasValue(R.styleable.ProgressBar_indeterminateTintMode)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index 143dea4..52e1728 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -79,8 +79,10 @@ public class RadialTimePickerView extends View { // Transparent alpha level private static final int ALPHA_TRANSPARENT = 0; - private static final int DEGREES_FOR_ONE_HOUR = 30; - private static final int DEGREES_FOR_ONE_MINUTE = 6; + private static final int HOURS_IN_DAY = 24; + private static final int MINUTES_IN_HOUR = 60; + private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_DAY; + private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_HOUR; private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; @@ -140,8 +142,7 @@ public class RadialTimePickerView extends View { private final float[] mInnerTextX = new float[12]; private final float[] mInnerTextY = new float[12]; - private final int[] mLineLength = new int[3]; - private final int[] mSelectionDegrees = new int[3]; + private final int[] mSelectionDegrees = new int[2]; private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>(); private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>(); @@ -168,13 +169,13 @@ public class RadialTimePickerView extends View { private int mYCenter; private int mCircleRadius; - private int mMinHypotenuseForInnerNumber; - private int mMaxHypotenuseForOuterNumber; - private int mHalfwayHypotenusePoint; + private int mMinDistForInnerNumber; + private int mMaxDistForOuterNumber; + private int mHalfwayDist; private String[] mOuterTextHours; private String[] mInnerTextHours; - private String[] mOuterTextMinutes; + private String[] mMinutesText; private AnimatorSet mTransition; private int mAmOrPm; @@ -462,11 +463,10 @@ public class RadialTimePickerView extends View { private void setCurrentHourInternal(int hour, boolean callback, boolean autoAdvance) { final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR; mSelectionDegrees[HOURS] = degrees; - mSelectionDegrees[HOURS_INNER] = degrees; // 0 is 12 AM (midnight) and 12 is 12 PM (noon). final int amOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM; - final boolean isOnInnerCircle = mIs24HourMode && hour >= 1 && hour <= 12; + final boolean isOnInnerCircle = getInnerCircleForHour(hour); if (mAmOrPm != amOrPm || mIsOnInnerCircle != isOnInnerCircle) { mAmOrPm = amOrPm; mIsOnInnerCircle = isOnInnerCircle; @@ -488,8 +488,7 @@ public class RadialTimePickerView extends View { * @return the current hour between 0 and 23 (inclusive) */ public int getCurrentHour() { - return getHourForDegrees( - mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS], mIsOnInnerCircle); + return getHourForDegrees(mSelectionDegrees[HOURS], mIsOnInnerCircle); } private int getHourForDegrees(int degrees, boolean innerCircle) { @@ -497,11 +496,11 @@ public class RadialTimePickerView extends View { if (mIs24HourMode) { // Convert the 12-hour value into 24-hour time based on where the // selector is positioned. - if (innerCircle && hour == 0) { - // Inner circle is 1 through 12. + if (!innerCircle && hour == 0) { + // Outer circle is 1 through 12. hour = 12; - } else if (!innerCircle && hour != 0) { - // Outer circle is 13 through 23 and 0. + } else if (innerCircle && hour != 0) { + // Inner circle is 13 through 23 and 0. hour += 12; } } else if (mAmOrPm == PM) { @@ -510,6 +509,9 @@ public class RadialTimePickerView extends View { return hour; } + /** + * @param hour the hour in 24-hour time or 12-hour time + */ private int getDegreesForHour(int hour) { // Convert to be 0-11. if (mIs24HourMode) { @@ -522,12 +524,19 @@ public class RadialTimePickerView extends View { return hour * DEGREES_FOR_ONE_HOUR; } + /** + * @param hour the hour in 24-hour time or 12-hour time + */ + private boolean getInnerCircleForHour(int hour) { + return mIs24HourMode && (hour == 0 || hour > 12); + } + public void setCurrentMinute(int minute) { setCurrentMinuteInternal(minute, true); } private void setCurrentMinuteInternal(int minute, boolean callback) { - mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE; + mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_HOUR) * DEGREES_FOR_ONE_MINUTE; invalidate(); @@ -572,6 +581,7 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } public void showMinutes(boolean animate) { @@ -587,6 +597,7 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } private void initHoursAndMinutesText() { @@ -608,7 +619,7 @@ public class RadialTimePickerView extends View { mInnerTextHours = mHours12Texts; } - mOuterTextMinutes = mMinutesTexts; + mMinutesText = mMinutesTexts; final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT; mAlpha[HOURS].setValue(hoursAlpha); @@ -627,9 +638,9 @@ public class RadialTimePickerView extends View { mYCenter = getHeight() / 2; mCircleRadius = Math.min(mXCenter, mYCenter); - mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius; - mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius; - mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2; + mMinDistForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius; + mMaxDistForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius; + mHalfwayDist = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2; calculatePositionsHours(); calculatePositionsMinutes(); @@ -674,6 +685,7 @@ public class RadialTimePickerView extends View { private void drawMinutes(Canvas canvas, float alphaMod) { final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f); if (minutesAlpha > 0) { + // Draw the minute selector under the elements. drawSelector(canvas, MINUTES, mSelectorPath, alphaMod); // Exclude the selector region, then draw minutes with no @@ -681,7 +693,7 @@ public class RadialTimePickerView extends View { canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE); drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], - mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], + mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, false, 0, false); canvas.restore(); @@ -690,7 +702,7 @@ public class RadialTimePickerView extends View { canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipPath(mSelectorPath, Region.Op.INTERSECT); drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], - mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], + mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, true, mSelectionDegrees[MINUTES], true); canvas.restore(); } @@ -718,7 +730,7 @@ public class RadialTimePickerView extends View { // Calculate the current radius at which to place the selection circle. final int selRadius = mSelectorRadius; final int selLength = mCircleRadius - mTextInset[index]; - final double selAngleRad = Math.toRadians(mSelectionDegrees[index]); + final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]); final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad); final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad); @@ -734,10 +746,10 @@ public class RadialTimePickerView extends View { } // Draw the dot if we're between two items. - final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0; + final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0; if (shouldDrawDot) { final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT]; - dotPaint.setColor(color); + dotPaint.setColor(mSelectorDotColor); canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint); } @@ -898,56 +910,43 @@ public class RadialTimePickerView extends View { } private int getDegreesFromXY(float x, float y, boolean constrainOutside) { - final double hypotenuse = Math.sqrt( - (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter)); + // Ensure the point is inside the touchable area. + final int innerBound; + final int outerBound; + if (mIs24HourMode && mShowHours) { + innerBound = mMinDistForInnerNumber; + outerBound = mMaxDistForOuterNumber; + } else { + final int index = mShowHours ? HOURS : MINUTES; + final int center = mCircleRadius - mTextInset[index]; + innerBound = center - mSelectorRadius; + outerBound = center + mSelectorRadius; + } - // Basic check if we're outside the range of the disk - if (constrainOutside && hypotenuse > mCircleRadius) { + final double dX = x - mXCenter; + final double dY = y - mYCenter; + final double distFromCenter = Math.sqrt(dX * dX + dY * dY); + if (distFromCenter < innerBound || constrainOutside && distFromCenter > outerBound) { return -1; } - // Check - if (mIs24HourMode && mShowHours) { - if (hypotenuse >= mMinHypotenuseForInnerNumber - && hypotenuse <= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = true; - } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside) - && hypotenuse >= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = false; - } else { - return -1; - } + // Convert to degrees. + final int degrees = (int) (Math.toDegrees(Math.atan2(dY, dX) + Math.PI / 2) + 0.5); + if (degrees < 0) { + return degrees + 360; } else { - final int index = (mShowHours) ? HOURS : MINUTES; - final float length = (mCircleRadius - mTextInset[index]); - final int distanceToNumber = (int) (hypotenuse - length); - final int maxAllowedDistance = mTextInset[index]; - if (distanceToNumber < -maxAllowedDistance - || (constrainOutside && distanceToNumber > maxAllowedDistance)) { - return -1; - } + return degrees; } + } - final float opposite = Math.abs(y - mYCenter); - int degrees = (int) (Math.toDegrees(Math.asin(opposite / hypotenuse)) + 0.5); - - // Now we have to translate to the correct quadrant. - final boolean rightSide = (x > mXCenter); - final boolean topSide = (y < mYCenter); - if (rightSide) { - if (topSide) { - degrees = 90 - degrees; - } else { - degrees = 90 + degrees; - } - } else { - if (topSide) { - degrees = 270 + degrees; - } else { - degrees = 270 - degrees; - } + private boolean getInnerCircleFromXY(float x, float y) { + if (mIs24HourMode && mShowHours) { + final double dX = x - mXCenter; + final double dY = y - mYCenter; + final double distFromCenter = Math.sqrt(dX * dX + dY * dY); + return distFromCenter <= mHalfwayDist; } - return degrees; + return false; } boolean mChangedDuringTouch = false; @@ -987,34 +986,28 @@ public class RadialTimePickerView extends View { private boolean handleTouchInput( float x, float y, boolean forceSelection, boolean autoAdvance) { - // Calling getDegreesFromXY has side effects, so cache - // whether we used to be on the inner circle. - final boolean wasOnInnerCircle = mIsOnInnerCircle; + final boolean isOnInnerCircle = getInnerCircleFromXY(x, y); final int degrees = getDegreesFromXY(x, y, false); if (degrees == -1) { return false; } - final int[] selectionDegrees = mSelectionDegrees; final int type; final int newValue; final boolean valueChanged; if (mShowHours) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; - valueChanged = selectionDegrees[HOURS] != snapDegrees - || selectionDegrees[HOURS_INNER] != snapDegrees - || wasOnInnerCircle != mIsOnInnerCircle; - - selectionDegrees[HOURS] = snapDegrees; - selectionDegrees[HOURS_INNER] = snapDegrees; + valueChanged = mIsOnInnerCircle != isOnInnerCircle + || mSelectionDegrees[HOURS] != snapDegrees; + mIsOnInnerCircle = isOnInnerCircle; + mSelectionDegrees[HOURS] = snapDegrees; type = HOURS; newValue = getCurrentHour(); } else { final int snapDegrees = snapPrefer30s(degrees) % 360; - valueChanged = selectionDegrees[MINUTES] != snapDegrees; - - selectionDegrees[MINUTES] = snapDegrees; + valueChanged = mSelectionDegrees[MINUTES] != snapDegrees; + mSelectionDegrees[MINUTES] = snapDegrees; type = MINUTES; newValue = getCurrentMinute(); } @@ -1132,17 +1125,11 @@ public class RadialTimePickerView extends View { @Override protected int getVirtualViewAt(float x, float y) { final int id; - - // Calling getDegreesXY() has side-effects, so we need to cache the - // current inner circle value and restore after the call. - final boolean wasOnInnerCircle = mIsOnInnerCircle; final int degrees = getDegreesFromXY(x, y, true); - final boolean isOnInnerCircle = mIsOnInnerCircle; - mIsOnInnerCircle = wasOnInnerCircle; - if (degrees != -1) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; if (mShowHours) { + final boolean isOnInnerCircle = getInnerCircleFromXY(x, y); final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle); final int hour = mIs24HourMode ? hour24 : hour24To12(hour24); id = makeId(TYPE_HOUR, hour); @@ -1153,8 +1140,10 @@ public class RadialTimePickerView extends View { // If the touched minute is closer to the current minute // than it is to the snapped minute, return current. + final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_HOUR); + final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_HOUR); final int minute; - if (Math.abs(current - touched) < Math.abs(snapped - touched)) { + if (currentOffset < snappedOffset) { minute = current; } else { minute = snapped; @@ -1168,6 +1157,20 @@ public class RadialTimePickerView extends View { return id; } + /** + * Returns the difference in degrees between two values along a circle. + * + * @param first value in the range [0,max] + * @param second value in the range [0,max] + * @param max the maximum value along the circle + * @return the difference in between the two values + */ + private int getCircularDiff(int first, int second, int max) { + final int diff = Math.abs(first - second); + final int midpoint = max / 2; + return (diff > midpoint) ? (max - diff) : diff; + } + @Override protected void getVisibleVirtualViews(IntArray virtualViewIds) { if (mShowHours) { @@ -1178,7 +1181,7 @@ public class RadialTimePickerView extends View { } } else { final int current = getCurrentMinute(); - for (int i = 0; i < 60; i += MINUTE_INCREMENT) { + for (int i = 0; i < MINUTES_IN_HOUR; i += MINUTE_INCREMENT) { virtualViewIds.add(makeId(TYPE_MINUTE, i)); // If the current minute falls between two increments, @@ -1236,7 +1239,7 @@ public class RadialTimePickerView extends View { if (value < current && nextValue > current) { // The current value is between two snap values. return makeId(type, current); - } else if (nextValue < 60) { + } else if (nextValue < MINUTES_IN_HOUR) { return makeId(type, nextValue); } } @@ -1290,7 +1293,7 @@ public class RadialTimePickerView extends View { final float centerRadius; final float degrees; if (type == TYPE_HOUR) { - final boolean innerCircle = mIs24HourMode && value > 0 && value <= 12; + final boolean innerCircle = getInnerCircleForHour(value); if (innerCircle) { centerRadius = mCircleRadius - mTextInset[HOURS_INNER]; radius = mSelectorRadius; diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java new file mode 100644 index 0000000..6ac0d89 --- /dev/null +++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup; + +import android.app.AppGlobals; +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; +import com.android.org.bouncycastle.util.Arrays; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +public class PreferredActivityBackupHelper implements BackupHelper { + private static final String TAG = "PreferredBackup"; + private static final boolean DEBUG = true; + + // current schema of the backup state blob + private static final int STATE_VERSION = 1; + + // key under which the preferred-activity state blob is committed to backup + private static final String KEY_PREFERRED = "preferred-activity"; + + final Context mContext; + + public PreferredActivityBackupHelper(Context context) { + mContext = context; + } + + // The fds passed here are shared among all helpers, so we mustn't close them + private void writeState(ParcelFileDescriptor stateFile, byte[] payload) { + try { + FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); + + // We explicitly don't close 'out' because we must not close the backing fd. + // The FileOutputStream will not close it implicitly. + @SuppressWarnings("resource") + DataOutputStream out = new DataOutputStream(fos); + + out.writeInt(STATE_VERSION); + if (payload == null) { + out.writeInt(0); + } else { + out.writeInt(payload.length); + out.write(payload); + } + } catch (IOException e) { + Slog.e(TAG, "Unable to write updated state", e); + } + } + + private byte[] readState(ParcelFileDescriptor oldStateFd) { + FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); + BufferedInputStream bis = new BufferedInputStream(fis); + + @SuppressWarnings("resource") + DataInputStream in = new DataInputStream(bis); + + byte[] oldState = null; + try { + int version = in.readInt(); + if (version == STATE_VERSION) { + int size = in.readInt(); + if (size > 0) { + if (size > 200*1024) { + Slog.w(TAG, "Suspiciously large state blog; ignoring. N=" + size); + } else { + // size looks okay; make the return buffer and fill it + oldState = new byte[size]; + in.read(oldState); + } + } + } else { + Slog.w(TAG, "Prior state from unrecognized version " + version); + } + } catch (EOFException e) { + // Empty file is expected on first backup, so carry on. If the state + // is truncated we just treat it the same way. + oldState = null; + } catch (Exception e) { + Slog.w(TAG, "Error examing prior backup state " + e.getMessage()); + oldState = null; + } + + return oldState; + } + + @Override + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + byte[] payload = null; + try { + byte[] oldPayload = readState(oldState); + + IPackageManager pm = AppGlobals.getPackageManager(); + byte[] newPayload = pm.getPreferredActivityBackup(UserHandle.USER_OWNER); + if (!Arrays.areEqual(oldPayload, newPayload)) { + if (DEBUG) { + Slog.i(TAG, "State has changed => writing new preferred app payload"); + } + data.writeEntityHeader(KEY_PREFERRED, newPayload.length); + data.writeEntityData(newPayload, newPayload.length); + } else { + if (DEBUG) { + Slog.i(TAG, "No change to state => not writing to wire"); + } + } + + // Always need to re-record the state, even if nothing changed + payload = newPayload; + } catch (Exception e) { + // On failures we'll wind up committing a zero-size state payload. This is + // a forward-safe situation because we know we commit the entire new payload + // on prior-state mismatch. + Slog.w(TAG, "Unable to record preferred activities", e); + } finally { + writeState(newState, payload); + } + } + + @Override + public void restoreEntity(BackupDataInputStream data) { + IPackageManager pm = AppGlobals.getPackageManager(); + try { + byte[] payload = new byte[data.size()]; + data.read(payload); + if (DEBUG) { + Slog.i(TAG, "Restoring preferred activities; size=" + payload.length); + } + pm.restorePreferredActivities(payload, UserHandle.USER_OWNER); + } catch (Exception e) { + Slog.e(TAG, "Exception reading restore data", e); + } + } + + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { + writeState(newState, null); + } + +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 037fd66..19d9e29 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -16,7 +16,6 @@ package com.android.server.backup; - import android.app.ActivityManagerNative; import android.app.IWallpaperManager; import android.app.backup.BackupDataInput; @@ -43,6 +42,13 @@ import java.io.IOException; public class SystemBackupAgent extends BackupAgentHelper { private static final String TAG = "SystemBackupAgent"; + // Names of the helper tags within the dataset. Changing one of these names will + // break the ability to restore from datasets that predate the change. + private static final String WALLPAPER_HELPER = "wallpaper"; + private static final String RECENTS_HELPER = "recents"; + private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; + private static final String PREFERRED_HELPER = "preferred_activities"; + // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are // taken to support the legacy backed-up datasets. @@ -84,10 +90,10 @@ public class SystemBackupAgent extends BackupAgentHelper { Slog.e(TAG, "Couldn't get wallpaper name\n" + re); } } - addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys)); - addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); - addHelper("account_sync_settings", - new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); + addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys)); + addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); super.onBackup(oldState, data, newState); } @@ -113,15 +119,15 @@ public class SystemBackupAgent extends BackupAgentHelper { public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // On restore, we also support a previous data schema "system_files" - addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, + addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }, new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} )); - addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this, + addHelper("system_files", new WallpaperBackupHelper(this, new String[] { WALLPAPER_IMAGE }, new String[] { WALLPAPER_IMAGE_KEY} )); - addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); - addHelper("account_sync_settings", - new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); + addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); try { super.onRestore(data, appVersionCode, newState); diff --git a/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 2fa6d7e..0000000 --- a/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 070bdbf..0000000 --- a/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 0d2836d..0000000 --- a/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index b8ac46d..0000000 --- a/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable/list_divider_material.xml b/core/res/res/drawable/list_divider_material.xml index babb646..658a59d 100644 --- a/core/res/res/drawable/list_divider_material.xml +++ b/core/res/res/drawable/list_divider_material.xml @@ -14,7 +14,10 @@ limitations under the License. --> -<nine-patch xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/list_divider_mtrl_alpha" - android:tint="?attr/colorForeground" - android:alpha="0.12" /> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:tint="?attr/colorForeground"> + <solid android:color="#1f000000" /> + <size + android:height="1dp" + android:width="1dp" /> +</shape> diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index 42b5d5d..5c3b90b 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -181,6 +181,18 @@ </family> <family> <fileset> + <file variant="elegant">NotoSansOriya-Regular.ttf</file> + <file variant="elegant">NotoSansOriya-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="compact">NotoSansOriyaUI-Regular.ttf</file> + <file variant="compact">NotoSansOriyaUI-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> <file>NotoSansSinhala-Regular.ttf</file> <file>NotoSansSinhala-Bold.ttf</file> </fileset> @@ -355,11 +367,6 @@ </family> <family> <fileset> - <file>Lohit-Odia.ttf</file> - </fileset> - </family> - <family> - <fileset> <file lang="zh-Hans">NotoSansHans-Regular.otf</file> </fileset> </family> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 37527e9..f3a7647 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -191,6 +191,14 @@ <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font> <font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font> </family> + <family variant="elegant"> + <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font> + <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> + </family> + <family variant="compact"> + <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font> + <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> + </family> <family> <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font> <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font> @@ -299,9 +307,6 @@ <family> <font weight="400" style="normal">NotoSansYi-Regular.ttf</font> </family> - <family> - <font weight="400" style="normal">Lohit-Odia.ttf</font> - </family> <family lang="zh-Hans"> <font weight="400" style="normal">NotoSansHans-Regular.otf</font> </family> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 64b4452..dd2368f 100755..100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -527,7 +527,11 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe void onUuidChanged() { updateProfiles(); ParcelUuid[] uuids = mDevice.getUuids(); + long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT; + if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) { + timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT; + } if (DEBUG) { Log.d(TAG, "onUuidChanged: Time since last connect" @@ -535,14 +539,12 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe } /* - * If a connect was attempted earlier without any UUID, we will do the - * connect now. + * If a connect was attempted earlier without any UUID, we will do the connect now. + * Otherwise, allow the connect on UUID change. */ - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) { - timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT; - } if (!mProfiles.isEmpty() - && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) { + && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime() + || (mConnectAttempted == 0))) { connectWithoutResettingTimer(false); } dispatchAttributesChanged(); diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index 0f967fc..de1c497 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -47,6 +47,15 @@ public class FieldPacker { // subAlign() can never work correctly for copied FieldPacker objects. } + static FieldPacker createFromArray(Object[] args) { + FieldPacker fp = new FieldPacker(RenderScript.sPointerSize * 8); + for (Object arg : args) { + fp.addSafely(arg); + } + fp.resize(fp.mPos); + return fp; + } + public void align(int v) { if ((v <= 0) || ((v & (v - 1)) != 0)) { throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v); @@ -618,294 +627,182 @@ public class FieldPacker { return mPos; } - private static void addToPack(FieldPacker fp, Object obj) { + private void add(Object obj) { if (obj instanceof Boolean) { - fp.addBoolean(((Boolean)obj).booleanValue()); + addBoolean((Boolean)obj); return; } if (obj instanceof Byte) { - fp.addI8(((Byte)obj).byteValue()); + addI8((Byte)obj); return; } if (obj instanceof Short) { - fp.addI16(((Short)obj).shortValue()); + addI16((Short)obj); return; } if (obj instanceof Integer) { - fp.addI32(((Integer)obj).intValue()); + addI32((Integer)obj); return; } if (obj instanceof Long) { - fp.addI64(((Long)obj).longValue()); + addI64((Long)obj); return; } if (obj instanceof Float) { - fp.addF32(((Float)obj).floatValue()); + addF32((Float)obj); return; } if (obj instanceof Double) { - fp.addF64(((Double)obj).doubleValue()); + addF64((Double)obj); return; } if (obj instanceof Byte2) { - fp.addI8((Byte2)obj); + addI8((Byte2)obj); return; } if (obj instanceof Byte3) { - fp.addI8((Byte3)obj); + addI8((Byte3)obj); return; } if (obj instanceof Byte4) { - fp.addI8((Byte4)obj); + addI8((Byte4)obj); return; } if (obj instanceof Short2) { - fp.addI16((Short2)obj); + addI16((Short2)obj); return; } if (obj instanceof Short3) { - fp.addI16((Short3)obj); + addI16((Short3)obj); return; } if (obj instanceof Short4) { - fp.addI16((Short4)obj); + addI16((Short4)obj); return; } if (obj instanceof Int2) { - fp.addI32((Int2)obj); + addI32((Int2)obj); return; } if (obj instanceof Int3) { - fp.addI32((Int3)obj); + addI32((Int3)obj); return; } if (obj instanceof Int4) { - fp.addI32((Int4)obj); + addI32((Int4)obj); return; } if (obj instanceof Long2) { - fp.addI64((Long2)obj); + addI64((Long2)obj); return; } if (obj instanceof Long3) { - fp.addI64((Long3)obj); + addI64((Long3)obj); return; } if (obj instanceof Long4) { - fp.addI64((Long4)obj); + addI64((Long4)obj); return; } if (obj instanceof Float2) { - fp.addF32((Float2)obj); + addF32((Float2)obj); return; } if (obj instanceof Float3) { - fp.addF32((Float3)obj); + addF32((Float3)obj); return; } if (obj instanceof Float4) { - fp.addF32((Float4)obj); + addF32((Float4)obj); return; } if (obj instanceof Double2) { - fp.addF64((Double2)obj); + addF64((Double2)obj); return; } if (obj instanceof Double3) { - fp.addF64((Double3)obj); + addF64((Double3)obj); return; } if (obj instanceof Double4) { - fp.addF64((Double4)obj); + addF64((Double4)obj); return; } if (obj instanceof Matrix2f) { - fp.addMatrix((Matrix2f)obj); + addMatrix((Matrix2f)obj); return; } if (obj instanceof Matrix3f) { - fp.addMatrix((Matrix3f)obj); + addMatrix((Matrix3f)obj); return; } if (obj instanceof Matrix4f) { - fp.addMatrix((Matrix4f)obj); + addMatrix((Matrix4f)obj); return; } if (obj instanceof BaseObj) { - fp.addObj((BaseObj)obj); + addObj((BaseObj)obj); return; } } - private static int getPackedSize(Object obj) { - if (obj instanceof Boolean) { - return 1; - } - - if (obj instanceof Byte) { - return 1; - } - - if (obj instanceof Short) { - return 2; - } - - if (obj instanceof Integer) { - return 4; - } - - if (obj instanceof Long) { - return 8; - } - - if (obj instanceof Float) { - return 4; - } - - if (obj instanceof Double) { - return 8; - } - - if (obj instanceof Byte2) { - return 2; - } - - if (obj instanceof Byte3) { - return 3; - } - - if (obj instanceof Byte4) { - return 4; - } - - if (obj instanceof Short2) { - return 4; - } - - if (obj instanceof Short3) { - return 6; - } - - if (obj instanceof Short4) { - return 8; - } - - if (obj instanceof Int2) { - return 8; - } - - if (obj instanceof Int3) { - return 12; - } - - if (obj instanceof Int4) { - return 16; - } - - if (obj instanceof Long2) { - return 16; - } - - if (obj instanceof Long3) { - return 24; - } - - if (obj instanceof Long4) { - return 32; - } - - if (obj instanceof Float2) { - return 8; - } - - if (obj instanceof Float3) { - return 12; + private boolean resize(int newSize) { + if (newSize == mLen) { + return false; } - if (obj instanceof Float4) { - return 16; - } - - if (obj instanceof Double2) { - return 16; - } - - if (obj instanceof Double3) { - return 24; - } - - if (obj instanceof Double4) { - return 32; - } - - if (obj instanceof Matrix2f) { - return 16; - } - - if (obj instanceof Matrix3f) { - return 36; - } - - if (obj instanceof Matrix4f) { - return 64; - } - - if (obj instanceof BaseObj) { - if (RenderScript.sPointerSize == 8) { - return 32; - } else { - return 4; - } - } - - return 0; + byte[] newData = new byte[newSize]; + System.arraycopy(mData, 0, newData, 0, mPos); + mData = newData; + mLen = newSize; + return true; } - static FieldPacker createFieldPack(Object[] args) { - int len = 0; - for (Object arg : args) { - len += getPackedSize(arg); - } - FieldPacker fp = new FieldPacker(len); - for (Object arg : args) { - addToPack(fp, arg); - } - return fp; + private void addSafely(Object obj) { + boolean retry; + final int oldPos = mPos; + do { + retry = false; + try { + add(obj); + } catch (ArrayIndexOutOfBoundsException e) { + mPos = oldPos; + resize(mLen * 2); + retry = true; + } + } while (retry); } - private final byte mData[]; + private byte mData[]; private int mPos; private int mLen; private BitSet mAlignment; - } - - diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java index 9d73ac4..8b9f73e 100644 --- a/rs/java/android/renderscript/ScriptGroup2.java +++ b/rs/java/android/renderscript/ScriptGroup2.java @@ -126,7 +126,7 @@ public class ScriptGroup2 extends BaseObj { public Closure(RenderScript rs, Script.InvokeID invokeID, Object[] args, Map<Script.FieldID, Object> globals) { super(0, rs); - mFP = FieldPacker.createFieldPack(args); + mFP = FieldPacker.createFromArray(args); mArgs = args; mBindings = globals; diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlur.java b/rs/java/android/renderscript/ScriptIntrinsicBlur.java index 5c4edd3..60e2b6d 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicBlur.java +++ b/rs/java/android/renderscript/ScriptIntrinsicBlur.java @@ -34,7 +34,7 @@ public final class ScriptIntrinsicBlur extends ScriptIntrinsic { * Create an intrinsic for applying a blur to an allocation. The * default radius is 5.0. * - * Supported elements types are {@link Element#U8_4} + * Supported elements types are {@link Element#U8_4 Element#U8} * * @param rs The RenderScript context * @param e Element type for inputs and outputs diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 5cc59e5..96840a2 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -94,6 +94,7 @@ import com.android.server.AppWidgetBackupBridge; import com.android.server.EventLogTags; import com.android.server.SystemService; import com.android.server.backup.PackageManagerBackupAgent.Metadata; +import com.android.server.pm.PackageManagerService; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 43f74fc..4ac2b48 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -785,6 +785,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiDeviceInfo avr = getAvrDeviceInfo(); if (avr != null) { onNewAvrAdded(avr); + } else { + setSystemAudioMode(false, true); } } }); @@ -1615,10 +1617,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { removeAction(SystemAudioAutoInitiationAction.class); removeAction(SystemAudioStatusAction.class); removeAction(VolumeControlAction.class); - - // Turn off the mode but do not write it the settings, so that the next time TV powers on - // the system audio mode setting can be restored automatically. - setSystemAudioMode(false, false); } @ServiceThreadOnly diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 03e9ad5..8a9f3e1 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -42,7 +42,7 @@ public class AppIdleController extends StateController implements UsageStatsManagerInternal.AppIdleStateChangeListener { private static final String LOG_TAG = "AppIdleController"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; // Singleton factory private static Object sCreationLock = new Object(); diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index 4c6cb17..b3d7287 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -91,14 +91,20 @@ public class TimeController extends StateController { public synchronized void maybeStartTrackingJob(JobStatus job) { if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) { maybeStopTrackingJob(job); + boolean isInsert = false; ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size()); while (it.hasPrevious()) { JobStatus ts = it.previous(); if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) { // Insert + isInsert = true; break; } } + if(isInsert) + { + it.next(); + } it.add(job); maybeUpdateAlarms( job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3c99484..2ff1718 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -87,6 +87,7 @@ import com.android.server.Watchdog; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.storage.DeviceStorageMonitorInternal; +import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManager; @@ -189,11 +190,14 @@ import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.Xml; import android.view.Display; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -246,6 +250,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final boolean DEBUG_SETTINGS = false; static final boolean DEBUG_PREFERRED = false; static final boolean DEBUG_UPGRADE = false; + private static final boolean DEBUG_BACKUP = true; private static final boolean DEBUG_INSTALL = false; private static final boolean DEBUG_REMOVE = false; private static final boolean DEBUG_BROADCASTS = false; @@ -866,6 +871,9 @@ public class PackageManagerService extends IPackageManager.Stub { final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>(); int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows + // backup/restore of preferred activity state + private static final String TAG_PREFERRED_BACKUP = "pa"; + private final String mRequiredVerifierPackage; private final PackageUsage mPackageUsage = new PackageUsage(); @@ -12525,6 +12533,83 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Non-Binder method, support for the backup/restore mechanism: write the + * full set of preferred activities in its canonical XML format. Returns true + * on success; false otherwise. + */ + @Override + public byte[] getPreferredActivityBackup(int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call getPreferredActivityBackup()"); + } + + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + try { + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(dataStream, "utf-8"); + serializer.startDocument(null, true); + serializer.startTag(null, TAG_PREFERRED_BACKUP); + + synchronized (mPackages) { + mSettings.writePreferredActivitiesLPr(serializer, userId, true); + } + + serializer.endTag(null, TAG_PREFERRED_BACKUP); + serializer.endDocument(); + serializer.flush(); + } catch (Exception e) { + if (DEBUG_BACKUP) { + Slog.e(TAG, "Unable to write preferred activities for backup", e); + } + return null; + } + + return dataStream.toByteArray(); + } + + @Override + public void restorePreferredActivities(byte[] backup, int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call restorePreferredActivities()"); + } + + try { + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new ByteArrayInputStream(backup), null); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + if (type != XmlPullParser.START_TAG) { + // oops didn't find a start tag?! + if (DEBUG_BACKUP) { + Slog.e(TAG, "Didn't find start tag during restore"); + } + return; + } + + // this is supposed to be TAG_PREFERRED_BACKUP + if (!TAG_PREFERRED_BACKUP.equals(parser.getName())) { + if (DEBUG_BACKUP) { + Slog.e(TAG, "Found unexpected tag " + parser.getName()); + } + return; + } + + // skip interfering stuff, then we're aligned with the backing implementation + while ((type = parser.next()) == XmlPullParser.TEXT) { } + synchronized (mPackages) { + mSettings.readPreferredActivitiesLPw(parser, userId); + } + } catch (Exception e) { + if (DEBUG_BACKUP) { + Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage()); + } + } + } + @Override public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, int sourceUserId, int targetUserId, int flags) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 0d2ef89..6930965 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -45,15 +45,16 @@ import android.os.UserManager; import android.util.AtomicFile; import android.text.TextUtils; import android.util.LogPrinter; - import android.util.SparseBooleanArray; import android.util.SparseLongArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; +import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.PackageManagerService.DumpState; import java.io.FileNotFoundException; @@ -1108,7 +1109,13 @@ final class Settings { mExternalDatabaseVersion = CURRENT_DATABASE_VERSION; } - private void readPreferredActivitiesLPw(XmlPullParser parser, int userId) + /** + * Applies the preferred activity state described by the given XML. This code + * also supports the restore-from-backup code path. + * + * @see PreferredActivityBackupHelper + */ + void readPreferredActivitiesLPw(XmlPullParser parser, int userId) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; @@ -1399,6 +1406,11 @@ final class Settings { return components; } + /** + * Record the state of preferred activity configuration into XML. This is used both + * for recording packages.xml internally and for supporting backup/restore of the + * preferred activity configuration. + */ void writePreferredActivitiesLPr(XmlSerializer serializer, int userId, boolean full) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, "preferred-activities"); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d365759..796868c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -617,8 +617,8 @@ public class WindowManagerService extends IWindowManager.Stub static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; boolean mAnimateWallpaperWithTarget; - // We give a wallpaper up to 1000ms to finish drawing before playing app transitions. - static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 1000; + // We give a wallpaper up to 500ms to finish drawing before playing app transitions. + static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500; static final int WALLPAPER_DRAW_NORMAL = 0; static final int WALLPAPER_DRAW_PENDING = 1; static final int WALLPAPER_DRAW_TIMEOUT = 2; @@ -9064,41 +9064,40 @@ public class WindowManagerService extends IWindowManager.Stub goodToGo = false; } } -// Stuck in a state with mWallpaperDrawState == WALLPAPER_DRAW_PENDING without a timeout. Leave -// commented out until that is understood. -// if (goodToGo && isWallpaperVisible(mWallpaperTarget)) { -// boolean wallpaperGoodToGo = true; -// for (int curTokenIndex = mWallpaperTokens.size() - 1; -// curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) { -// WindowToken token = mWallpaperTokens.get(curTokenIndex); -// for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0; -// curWallpaperIndex--) { -// WindowState wallpaper = token.windows.get(curWallpaperIndex); -// if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) { -// // We've told this wallpaper to be visible, but it is not drawn yet -// wallpaperGoodToGo = false; -// if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { -// // wait for this wallpaper until it is drawn or timeout -// goodToGo = false; -// } -// if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { -// mWallpaperDrawState = WALLPAPER_DRAW_PENDING; -// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); -// mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT, -// WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); -// } -// if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, -// "Wallpaper should be visible but has not been drawn yet. " + -// "mWallpaperDrawState=" + mWallpaperDrawState); -// break; -// } -// } -// } -// if (wallpaperGoodToGo) { -// mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; -// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); -// } -// } + + if (goodToGo && isWallpaperVisible(mWallpaperTarget)) { + boolean wallpaperGoodToGo = true; + for (int curTokenIndex = mWallpaperTokens.size() - 1; + curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) { + WindowToken token = mWallpaperTokens.get(curTokenIndex); + for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0; + curWallpaperIndex--) { + WindowState wallpaper = token.windows.get(curWallpaperIndex); + if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) { + // We've told this wallpaper to be visible, but it is not drawn yet + wallpaperGoodToGo = false; + if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { + // wait for this wallpaper until it is drawn or timeout + goodToGo = false; + } + if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { + mWallpaperDrawState = WALLPAPER_DRAW_PENDING; + mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); + mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT, + WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); + } + if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, + "Wallpaper should be visible but has not been drawn yet. " + + "mWallpaperDrawState=" + mWallpaperDrawState); + break; + } + } + } + if (wallpaperGoodToGo) { + mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; + mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); + } + } } if (goodToGo) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); @@ -9890,7 +9889,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - winAnimator.setSurfaceBoundariesLocked(recoveringMemory); + winAnimator.prepareSurfaceLocked(recoveringMemory); final AppWindowToken atoken = w.mAppToken; if (DEBUG_STARTING_WINDOW && atoken != null diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index ac1b0f1..056267d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1341,7 +1341,7 @@ class WindowStateAnimator { } } - void setSurfaceBoundariesLocked(final boolean recoveringMemory) { + private void setSurfaceBoundariesLocked(final boolean recoveringMemory) { final WindowState w = mWin; int width; diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 235567c..4498b84 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -18,6 +18,7 @@ package com.android.server.usage; import android.app.usage.TimeSparseArray; import android.app.usage.UsageStatsManager; +import android.os.Build; import android.util.AtomicFile; import android.util.Slog; @@ -35,7 +36,7 @@ import java.util.List; * Provides an interface to query for UsageStat data from an XML database. */ class UsageStatsDatabase { - private static final int CURRENT_VERSION = 2; + private static final int CURRENT_VERSION = 3; private static final String TAG = "UsageStatsDatabase"; private static final boolean DEBUG = UsageStatsService.DEBUG; @@ -47,6 +48,8 @@ class UsageStatsDatabase { private final TimeSparseArray<AtomicFile>[] mSortedStatFiles; private final UnixCalendar mCal; private final File mVersionFile; + private boolean mFirstUpdate; + private boolean mNewUpdate; public UsageStatsDatabase(File dir) { mIntervalDirs = new File[] { @@ -73,7 +76,7 @@ class UsageStatsDatabase { } } - checkVersionLocked(); + checkVersionAndBuildLocked(); indexFilesLocked(); // Delete files that are in the future. @@ -194,10 +197,35 @@ class UsageStatsDatabase { } } - private void checkVersionLocked() { + /** + * Is this the first update to the system from L to M? + */ + boolean isFirstUpdate() { + return mFirstUpdate; + } + + /** + * Is this a system update since we started tracking build fingerprint in the version file? + */ + boolean isNewUpdate() { + return mNewUpdate; + } + + private void checkVersionAndBuildLocked() { int version; + String buildFingerprint; + String currentFingerprint = getBuildFingerprint(); + mFirstUpdate = true; + mNewUpdate = true; try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) { version = Integer.parseInt(reader.readLine()); + buildFingerprint = reader.readLine(); + if (buildFingerprint != null) { + mFirstUpdate = false; + } + if (currentFingerprint.equals(buildFingerprint)) { + mNewUpdate = false; + } } catch (NumberFormatException | IOException e) { version = 0; } @@ -205,9 +233,15 @@ class UsageStatsDatabase { if (version != CURRENT_VERSION) { Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION); doUpgradeLocked(version); + } + if (version != CURRENT_VERSION || mNewUpdate) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) { writer.write(Integer.toString(CURRENT_VERSION)); + writer.write("\n"); + writer.write(currentFingerprint); + writer.write("\n"); + writer.flush(); } catch (IOException e) { Slog.e(TAG, "Failed to write new version"); throw new RuntimeException(e); @@ -215,6 +249,12 @@ class UsageStatsDatabase { } } + private String getBuildFingerprint() { + return Build.VERSION.RELEASE + ";" + + Build.VERSION.CODENAME + ";" + + Build.VERSION.INCREMENTAL; + } + private void doUpgradeLocked(int thisVersion) { if (thisVersion < 2) { // Delete all files if we are version 0. This is a pre-release version, diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f458dbc..cc0ab81 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -92,7 +92,7 @@ public class UsageStatsService extends SystemService implements long mRealTimeSnapshot; long mSystemTimeSnapshot; - private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 3L * 24 * 60 * 60 * 1000; //3 days + private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 1L * 24 * 60 * 60 * 1000; // 1 day private long mAppIdleDurationMillis; private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener> diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index afe27c7..0a9481a 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -19,8 +19,11 @@ package com.android.server.usage; import android.app.usage.ConfigurationStats; import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; +import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.SystemClock; import android.content.Context; @@ -60,6 +63,7 @@ class UserUsageStatsService { private final UnixCalendar mDailyExpiryDate; private final StatsUpdatedListener mListener; private final String mLogPrefix; + private final int mUserId; interface StatsUpdatedListener { void onStatsUpdated(); @@ -73,6 +77,7 @@ class UserUsageStatsService { mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; mListener = listener; mLogPrefix = "User[" + Integer.toString(userId) + "] "; + mUserId = userId; } void init(final long currentTimeMillis) { @@ -128,6 +133,35 @@ class UserUsageStatsService { stat.updateConfigurationStats(null, stat.lastTimeSaved); } + + if (mDatabase.isNewUpdate()) { + initializeDefaultsForApps(currentTimeMillis, mDatabase.isFirstUpdate()); + } + } + + /** + * 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, boolean firstUpdate) { + PackageManager pm = mContext.getPackageManager(); + List<PackageInfo> packages = pm.getInstalledPackages(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()) + && getLastPackageAccessTime(packageName) == -1) { + for (IntervalStats stats : mCurrentStats) { + stats.update(packageName, currentTimeMillis, Event.INTERACTION); + mStatsChanged = true; + } + } + } + // Persist the new OTA-related access stats. + persistActiveStats(); } void onTimeChanged(long oldTime, long newTime) { |
