diff options
56 files changed, 696 insertions, 211 deletions
diff --git a/api/current.txt b/api/current.txt index e295a63..cb63aaa 100644 --- a/api/current.txt +++ b/api/current.txt @@ -106,6 +106,7 @@ package android { field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"; field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO"; field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS"; + field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; @@ -26444,6 +26445,7 @@ package android.provider { field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; + field public static final java.lang.String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS"; field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS"; @@ -33956,6 +33958,7 @@ package android.util { field public static final int DENSITY_280 = 280; // 0x118 field public static final int DENSITY_360 = 360; // 0x168 field public static final int DENSITY_400 = 400; // 0x190 + field public static final int DENSITY_420 = 420; // 0x1a4 field public static final int DENSITY_560 = 560; // 0x230 field public static final int DENSITY_DEFAULT = 160; // 0xa0 field public static final int DENSITY_HIGH = 240; // 0xf0 diff --git a/api/system-current.txt b/api/system-current.txt index 8b9af5e..a9bc944 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -180,6 +180,7 @@ package android { field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES"; field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS"; + field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; @@ -28502,6 +28503,7 @@ package android.provider { field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; + field public static final java.lang.String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS"; field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS"; @@ -36248,6 +36250,7 @@ package android.util { field public static final int DENSITY_280 = 280; // 0x118 field public static final int DENSITY_360 = 360; // 0x168 field public static final int DENSITY_400 = 400; // 0x190 + field public static final int DENSITY_420 = 420; // 0x1a4 field public static final int DENSITY_560 = 560; // 0x230 field public static final int DENSITY_DEFAULT = 160; // 0xa0 field public static final int DENSITY_HIGH = 240; // 0xf0 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index fd88a05..da21eaf 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4235,11 +4235,6 @@ public final class ActivityThread { configDiff = mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); - - final Theme systemTheme = getSystemContext().getTheme(); - if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { - systemTheme.rebase(); - } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index abe12dc..7824072 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -1082,7 +1082,8 @@ public class AlertDialog extends Dialog implements DialogInterface { * create and display the dialog. */ public AlertDialog create() { - final AlertDialog dialog = new AlertDialog(P.mContext); + // Context has already been wrapped with the appropriate theme. + final AlertDialog dialog = new AlertDialog(P.mContext, 0, false); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c505b0b..e3414d9 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3204,8 +3204,13 @@ public class DevicePolicyManager { * Called by a profile or device owner to set the application restrictions for a given target * application running in the profile. * - * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be - * boolean, int, String, or String[]. + * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be: + * <ul> + * <li>{@code boolean} + * <li>{@code int} + * <li>{@code String} or {@code String[]} + * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]} + * </ul> * * <p>The application restrictions are only made visible to the target application and the * profile or device owner. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6e178a4..fe95864 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -616,20 +616,46 @@ public final class Settings { /** * Activity Action: Show screen for controlling which apps can ignore battery optimizations. * <p> - * Input: Optionally, the Intent's data URI specifies the application package name - * to be shown, with the "package" scheme. That is "package:com.my.app". + * Input: Nothing. * <p> * Output: Nothing. * <p> * You can use {@link android.os.PowerManager#isIgnoringBatteryOptimizations * PowerManager.isIgnoringBatteryOptimizations()} to determine if an application is - * already ignoring optimizations. + * already ignoring optimizations. You can use + * {@link #ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS} to ask the user to put you + * on this list. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS"; /** + * Activity Action: Ask the user to allow an to ignore battery optimizations (that is, + * put them on the whitelist of apps shown by + * {@link #ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}). For an app to use this, it also + * must hold the {@link android.Manifest.permission#REQUEST_IGNORE_BATTERY_OPTIMIZATIONS} + * permission. + * <p><b>Note:</b> most applications should <em>not</em> use this; there are many facilities + * provided by the platform for applications to operate correctly in the various power + * saving mode. This is only for unusual applications that need to deeply control their own + * execution, at the potential expense of the user's battery life. Note that these applications + * greatly run the risk of showing to the user has how power consumers on their device.</p> + * <p> + * Input: The Intent's data URI must specify the application package name + * to be shown, with the "package" scheme. That is "package:com.my.app". + * <p> + * Output: Nothing. + * <p> + * You can use {@link android.os.PowerManager#isIgnoringBatteryOptimizations + * PowerManager.isIgnoringBatteryOptimizations()} to determine if an application is + * already ignoring optimizations. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = + "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; + + /** * @hide * Activity Action: Show the "app ops" settings screen. * <p> diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index a36e66c..9a69600 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -90,6 +90,14 @@ public class DisplayMetrics { public static final int DENSITY_400 = 400; /** + * Intermediate density for screens that sit somewhere between + * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi). + * This is not a density that applications should target, instead relying + * on the system to scale their {@link #DENSITY_XXHIGH} assets for them. + */ + public static final int DENSITY_420 = 420; + + /** * Standard quantized DPI for extra-extra-high-density screens. */ public static final int DENSITY_XXHIGH = 480; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index dd7ea45..a7bdbe0 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2907,13 +2907,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mLastHasRightStableInset = hasRightStableInset; } - updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor, - mLastTopInset, false /* matchVertical */, animate && !disallowAnimate); - boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0; int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset; updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor, - navBarSize, navBarToRightEdge, animate && !disallowAnimate); + navBarSize, navBarToRightEdge, 0 /* rightInset */, + animate && !disallowAnimate); + + boolean statusBarNeedsRightInset = navBarToRightEdge + && mNavigationColorViewState.present; + int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0; + updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor, + mLastTopInset, false /* matchVertical */, statusBarRightInset, + animate && !disallowAnimate); } // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need @@ -2966,15 +2971,17 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @param size the current size in the non-parent-matching dimension. * @param verticalBar if true the view is attached to a vertical edge, otherwise to a * horizontal edge, + * @param rightMargin rightMargin for the color view. * @param animate if true, the change will be animated. */ private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, - int size, boolean verticalBar, boolean animate) { - boolean show = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0 + int size, boolean verticalBar, int rightMargin, boolean animate) { + state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0 && (getAttributes().flags & state.hideWindowFlag) == 0 - && (getAttributes().flags & state.translucentFlag) == 0 - && (color & Color.BLACK) != 0 && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + boolean show = state.present + && (color & Color.BLACK) != 0 + && (getAttributes().flags & state.translucentFlag) == 0; boolean visibilityChanged = false; View view = state.view; @@ -2993,7 +3000,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { view.setVisibility(INVISIBLE); state.targetVisibility = VISIBLE; - addView(view, new LayoutParams(resolvedWidth, resolvedHeight, resolvedGravity)); + LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight, + resolvedGravity); + lp.rightMargin = rightMargin; + addView(view, lp); updateColorViewTranslations(); } } else { @@ -3003,10 +3013,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (show) { LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (lp.height != resolvedHeight || lp.width != resolvedWidth - || lp.gravity != resolvedGravity) { + || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { lp.height = resolvedHeight; lp.width = resolvedWidth; lp.gravity = resolvedGravity; + lp.rightMargin = rightMargin; view.setLayoutParams(lp); } view.setBackgroundColor(color); @@ -5022,6 +5033,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private static class ColorViewState { View view = null; int targetVisibility = View.INVISIBLE; + boolean present = false; final int id; final int systemUiHideFlag; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 5e5450e..6b07a47 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -269,6 +269,10 @@ LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunre # is not being compiled with that level. Remove once this has changed. LOCAL_CFLAGS += -Wno-c++11-extensions +# b/22414716: thread_local (android/graphics/Paint.cpp) and Clang don't like each other at the +# moment. +LOCAL_CLANG := false + include $(BUILD_SHARED_LIBRARY) include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index bae2cde..a3d5b8a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -43,6 +43,9 @@ #include <dirent.h> #include <assert.h> +#include <string> +#include <vector> + using namespace android; @@ -357,38 +360,66 @@ static bool hasFile(const char* file) { return false; } +// Convenience wrapper over the property API that returns an +// std::string. +std::string getProperty(const char* key, const char* defaultValue) { + std::vector<char> temp(PROPERTY_VALUE_MAX); + const int len = property_get(key, &temp[0], defaultValue); + if (len < 0) { + return ""; + } + return std::string(&temp[0], len); +} + /* - * Read the persistent locale. Attempts to read to persist.sys.locale - * and falls back to the default locale (ro.product.locale) if - * persist.sys.locale is empty. + * Read the persistent locale. Inspects the following system properties + * (in order) and returns the first non-empty property in the list : + * + * (1) persist.sys.locale + * (2) persist.sys.language/country/localevar (country and localevar are + * inspected iff. language is non-empty. + * (3) ro.product.locale + * (4) ro.product.locale.language/region + * + * Note that we need to inspect persist.sys.language/country/localevar to + * preserve language settings for devices that are upgrading from Lollipop + * to M. The same goes for ro.product.locale.language/region as well. */ -static void readLocale(char* locale) +const std::string readLocale() { - // Allocate 4 extra bytes because we might read a property into - // this array at offset 4. - char propLocale[PROPERTY_VALUE_MAX + 4]; - - property_get("persist.sys.locale", propLocale, ""); - if (propLocale[0] == 0) { - property_get("ro.product.locale", propLocale, ""); - - if (propLocale[0] == 0) { - // If persist.sys.locale and ro.product.locale are missing, - // construct a locale value from the individual locale components. - property_get("ro.product.locale.language", propLocale, "en"); - - // The language code is either two or three chars in length. If it - // isn't 2 chars long, assume three. Anything else is an error - // anyway. - const int offset = (propLocale[2] == 0) ? 2 : 3; - propLocale[offset] = '-'; - - property_get("ro.product.locale.region", propLocale + offset + 1, "US"); + const std::string locale = getProperty("persist.sys.locale", ""); + if (!locale.empty()) { + return locale; + } + + const std::string language = getProperty("persist.sys.language", ""); + if (!language.empty()) { + const std::string country = getProperty("persist.sys.country", ""); + const std::string variant = getProperty("persist.sys.localevar", ""); + + std::string out = language; + if (!country.empty()) { + out = out + "-" + country; + } + + if (!variant.empty()) { + out = out + "-" + variant; } + + return out; } - strncat(locale, propLocale, PROPERTY_VALUE_MAX); - // ALOGD("[DEBUG] locale=%s", locale); + const std::string productLocale = getProperty("ro.product.locale", ""); + if (!productLocale.empty()) { + return productLocale; + } + + // If persist.sys.locale and ro.product.locale are missing, + // construct a locale value from the individual locale components. + const std::string productLanguage = getProperty("ro.product.locale.language", "en"); + const std::string productRegion = getProperty("ro.product.locale.region", "US"); + + return productLanguage + "-" + productRegion; } void AndroidRuntime::addOption(const char* optionString, void* extraInfo) @@ -793,7 +824,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) /* Set the properties for locale */ { strcpy(localeOption, "-Duser.locale="); - readLocale(localeOption); + const std::string locale = readLocale(); + strncat(localeOption, locale.c_str(), PROPERTY_VALUE_MAX); addOption(localeOption); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 53b553e8..629d14b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2257,6 +2257,13 @@ <permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" android:protectionLevel="system|signature" /> + <!-- Permission an application must hold in order to use + {@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}. + This is a normal permission: an app requesting it will always be granted the + permission, without the user needing to approve or see it. --> + <permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" + android:protectionLevel="normal" /> + <!-- @SystemApi Allows an application to collect battery statistics --> <permission android:name="android.permission.BATTERY_STATS" android:protectionLevel="signature|privileged|development" /> diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index bf069d3..32f6a89 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -786,12 +786,16 @@ public class RippleDrawable extends LayerDrawable { mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN); } - // Draw the appropriate mask. + // Draw the appropriate mask anchored to (0,0). + final int left = bounds.left; + final int top = bounds.top; + mMaskCanvas.translate(-left, -top); if (maskType == MASK_EXPLICIT) { drawMask(mMaskCanvas); } else if (maskType == MASK_CONTENT) { drawContent(mMaskCanvas); } + mMaskCanvas.translate(left, top); } private int getMaskType() { diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 7605231..f42d750 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -38,27 +38,35 @@ import javax.security.auth.x500.X500Principal; /** * {@link AlgorithmParameterSpec} for initializing a {@link KeyPairGenerator} or a * {@link KeyGenerator} of the <a href="{@docRoot}training/articles/keystore.html">Android Keystore - * system</a>. The spec determines whether user authentication is required for using the key, what - * uses the key is authorized for (e.g., only for signing -- decryption not permitted), the key's - * validity start and end dates. + * system</a>. The spec determines authorized uses of the key, such as whether user authentication + * is required for using the key, what operations are authorized (e.g., signing, but not + * decryption) and with what parameters (e.g., only with a particular padding scheme or digest), the + * key's validity start and end dates. Key use authorizations expressed in the spec apply only to + * secret keys and private keys -- public keys can be used for any supported operations. * * <p>To generate an asymmetric key pair or a symmetric key, create an instance of this class using * the {@link Builder}, initialize a {@code KeyPairGenerator} or a {@code KeyGenerator} of the * desired key type (e.g., {@code EC} or {@code AES} -- see * {@link KeyProperties}.{@code KEY_ALGORITHM} constants) from the {@code AndroidKeyStore} provider - * with the {@code KeyPairGeneratorSpec} instance, and then generate a key or key pair using - * {@link KeyPairGenerator#generateKeyPair()}. + * with the {@code KeyGenParameterSpec} instance, and then generate a key or key pair using + * {@link KeyGenerator#generateKey()} or {@link KeyPairGenerator#generateKeyPair()}. * * <p>The generated key pair or key will be returned by the generator and also stored in the Android - * Keystore system under the alias specified in this spec. To obtain the secret or private key from - * the Android KeyStore use {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} + * Keystore under the alias specified in this spec. To obtain the secret or private key from the + * Android Keystore use {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} * or {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}. - * To obtain the public key from the Android Keystore system use + * To obtain the public key from the Android Keystore use * {@link java.security.KeyStore#getCertificate(String)} and then * {@link Certificate#getPublicKey()}. * + * <p>To help obtain algorithm-specific public parameters of key pairs stored in the Android + * Keystore, generated private keys implement {@link java.security.interfaces.ECKey} or + * {@link java.security.interfaces.RSAKey} interfaces whereas public keys implement + * {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey} + * interfaces. + * * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in - * the Android KeyStore. This is because the {@link java.security.KeyStore} abstraction does not + * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not * support storing key pairs without a certificate. The subject, serial number, and validity dates * of the certificate can be customized in this spec. The self-signed certificate may be replaced at * a later time by a certificate signed by a Certificate Authority (CA). @@ -82,27 +90,60 @@ import javax.security.auth.x500.X500Principal; * * <p>Instances of this class are immutable. * - * <p><h3>Example: Asymmetric key pair</h3> - * The following example illustrates how to generate an EC key pair in the Android KeyStore system - * under alias {@code key1} authorized to be used only for signing using SHA-256, SHA-384, - * or SHA-512 digest and only if the user has been authenticated within the last five minutes. + * <p><h3>Example: NIST P-256 EC key pair for signing/verification using ECDSA</h3> + * This example illustrates how to generate a NIST P-256 (aka secp256r1 aka prime256v1) EC key pair + * in the Android KeyStore system under alias {@code key1} where the private key is authorized to be + * used only for signing using SHA-256, SHA-384, or SHA-512 digest and only if the user has been + * authenticated within the last five minutes. The use of public key is unrestricted, thus + * permitting signature verification using any padding schemes and digests, and without user + * authentication. * <pre> {@code * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( - * KeyProperties.KEY_ALGORITHM_EC, - * "AndroidKeyStore"); + * KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); * keyPairGenerator.initialize( * new KeyGenParameterSpec.Builder( * "key1", - * KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) + * KeyProperties.PURPOSE_SIGN) + * .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) * .setDigests(KeyProperties.DIGEST_SHA256, * KeyProperties.DIGEST_SHA384, * KeyProperties.DIGEST_SHA512) - * // Only permit this key to be used if the user authenticated + * // Only permit the private key to be used if the user authenticated * // within the last five minutes. * .setUserAuthenticationRequired(true) * .setUserAuthenticationValidityDurationSeconds(5 * 60) * .build()); * KeyPair keyPair = keyPairGenerator.generateKeyPair(); + * Signature signature = Signature.getInstance("SHA256withECDSA"); + * signature.initSign(keyPair.getPrivate()); + * ... + * + * // The key pair can also be obtained from the Android Keystore any time as follows: + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null); + * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey(); + * }</pre> + * + * <p><h3>Example: RSA key pair for signing/verification using RSA-PSS</h3> + * This example illustrates how to generate an RSA key pair in the Android KeyStore system under + * alias {@code key1} authorized to be used only for signing using the RSA-PSS signature padding + * scheme with SHA-256 or SHA-512 digests. The use of public key is unrestricted, thus permitting + * signature verification using any padding schemes and digests. + * <pre> {@code + * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( + * KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); + * keyPairGenerator.initialize( + * new KeyGenParameterSpec.Builder( + * "key1", + * KeyProperties.PURPOSE_SIGN) + * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + * .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS) + * .build()); + * KeyPair keyPair = keyPairGenerator.generateKeyPair(); + * Signature signature = Signature.getInstance("SHA256withRSA/PSS"); + * signature.initSign(keyPair.getPrivate()); + * ... * * // The key pair can also be obtained from the Android Keystore any time as follows: * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); @@ -111,14 +152,40 @@ import javax.security.auth.x500.X500Principal; * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey(); * }</pre> * - * <p><h3>Example: Symmetric key</h3> + * <p><h3>Example: RSA key pair for encryption/decryption using RSA OAEP</h3> + * This example illustrates how to generate an RSA key pair in the Android KeyStore system under + * alias {@code key1} where the private key is authorized to be used only for decryption using RSA + * OAEP encryption padding scheme with SHA-256 or SHA-512 digests. The use of public key is + * unrestricted, thus permitting encryption using any padding schemes and digests. + * <pre> {@code + * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( + * KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); + * keyPairGenerator.initialize( + * new KeyGenParameterSpec.Builder( + * "key1", + * KeyProperties.PURPOSE_DECRYPT) + * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) + * .build()); + * KeyPair keyPair = keyPairGenerator.generateKeyPair(); + * Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + * cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + * ... + * + * // The key pair can also be obtained from the Android Keystore any time as follows: + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null); + * PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey(); + * }</pre> + * + * <p><h3>Example: AES key for encryption/decryption in GCM mode</h3> * The following example illustrates how to generate an AES key in the Android KeyStore system under * alias {@code key2} authorized to be used only for encryption/decryption in GCM mode with no * padding. * <pre> {@code * KeyGenerator keyGenerator = KeyGenerator.getInstance( - * KeyProperties.KEY_ALGORITHM_AES, - * "AndroidKeyStore"); + * KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); * keyGenerator.initialize( * new KeyGenParameterSpec.Builder("key2", * KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) @@ -127,6 +194,29 @@ import javax.security.auth.x500.X500Principal; * .build()); * SecretKey key = keyGenerator.generateKey(); * + * Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + * cipher.init(Cipher.ENCRYPT_MODE, key); + * ... + * + * // The key can also be obtained from the Android Keystore any time as follows: + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * key = (SecretKey) keyStore.getKey("key2", null); + * }</pre> + * + * <p><h3>Example: HMAC key for generating a MAC using SHA-256</h3> + * This example illustrates how to generate an HMAC key in the Android KeyStore system under alias + * {@code key2} authorized to be used only for generating an HMAC using SHA-256. + * <pre> {@code + * KeyGenerator keyGenerator = KeyGenerator.getInstance( + * KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore"); + * keyGenerator.initialize( + * new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build()); + * SecretKey key = keyGenerator.generateKey(); + * Mac mac = Mac.getInstance("HmacSHA256"); + * mac.init(key); + * ... + * * // The key can also be obtained from the Android Keystore any time as follows: * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); * keyStore.load(null); diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index b71dc82..c984439 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -33,28 +33,36 @@ import javax.crypto.Mac; /** * Specification of how a key or key pair is secured when imported into the - * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. This class - * specifies parameters such as whether user authentication is required for using the key, what uses - * the key is authorized for (e.g., only in {@code GCM} mode, or only for signing -- decryption not - * permitted), the key's and validity start and end dates. + * <a href="{@docRoot}training/articles/keystore.html">Android Keystore system</a>. This class + * specifies authorized uses of the imported key, such as whether user authentication is required + * for using the key, what operations the key is authorized for (e.g., decryption, but not signing) + * and with what parameters (e.g., only with a particular padding scheme or digest), the key's and + * validity start and end dates. Key use authorizations expressed in this class apply only to secret + * keys and private keys -- public keys can be used for any supported operations. * - * <p>To import a key or key pair into the Android KeyStore, create an instance of this class using + * <p>To import a key or key pair into the Android Keystore, create an instance of this class using * the {@link Builder} and pass the instance into {@link java.security.KeyStore#setEntry(String, java.security.KeyStore.Entry, ProtectionParameter) KeyStore.setEntry} * with the key or key pair being imported. * - * <p>To obtain the secret/symmetric or private key from the Android KeyStore use + * <p>To obtain the secret/symmetric or private key from the Android Keystore use * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}. - * To obtain the public key from the Android KeyStore use + * To obtain the public key from the Android Keystore use * {@link java.security.KeyStore#getCertificate(String)} and then * {@link Certificate#getPublicKey()}. * - * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible. + * <p>To help obtain algorithm-specific public parameters of key pairs stored in the Android + * Keystore, its private keys implement {@link java.security.interfaces.ECKey} or + * {@link java.security.interfaces.RSAKey} interfaces whereas its public keys implement + * {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey} + * interfaces. + * + * <p>NOTE: The key material of keys stored in the Android Keystore is not accessible. * * <p>Instances of this class are immutable. * - * <p><h3>Example: Symmetric Key</h3> - * The following example illustrates how to import an AES key into the Android KeyStore under alias + * <p><h3>Example: AES key for encryption/decryption in GCM mode</h3> + * This example illustrates how to import an AES key into the Android KeyStore under alias * {@code key1} authorized to be used only for encryption/decryption in GCM mode with no padding. * The key must export its key material via {@link Key#getEncoded()} in {@code RAW} format. * <pre> {@code @@ -71,15 +79,41 @@ import javax.crypto.Mac; * .build()); * // Key imported, obtain a reference to it. * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null); - * // The original key can now be thrown away. + * // The original key can now be discarded. + * + * Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + * cipher.init(Cipher.ENCRYPT_MODE, keyStoreKey); + * ... * }</pre> * - * <p><h3>Example: Asymmetric Key Pair</h3> - * The following example illustrates how to import an EC key pair into the Android KeyStore under - * alias {@code key2} authorized to be used only for signing with SHA-256 digest and only if - * the user has been authenticated within the last ten minutes. Both the private and the public key - * must export their key material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} - * format respectively. + * <p><h3>Example: HMAC key for generating MACs using SHA-512</h3> + * This example illustrates how to import an HMAC key into the Android KeyStore under alias + * {@code key1} authorized to be used only for generating MACs using SHA-512 digest. The key must + * export its key material via {@link Key#getEncoded()} in {@code RAW} format. + * <pre> {@code + * SecretKey key = ...; // HMAC key of algorithm "HmacSHA512". + * + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * keyStore.setEntry( + * "key1", + * new KeyStore.SecretKeyEntry(key), + * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build()); + * // Key imported, obtain a reference to it. + * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null); + * // The original key can now be discarded. + * + * Mac mac = Mac.getInstance("HmacSHA512"); + * mac.init(keyStoreKey); + * ... + * }</pre> + * + * <p><h3>Example: EC key pair for signing/verification using ECDSA</h3> + * This example illustrates how to import an EC key pair into the Android KeyStore under alias + * {@code key2} with the private key authorized to be used only for signing with SHA-256 or SHA-512 + * digests. The use of public key is unrestricted, thus permitting signature verification using any + * digests. Both the private and the public key must export their key material via + * {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} format respectively. * <pre> {@code * PrivateKey privateKey = ...; // EC private key * Certificate[] certChain = ...; // Certificate chain with the first certificate @@ -91,7 +125,39 @@ import javax.crypto.Mac; * "key2", * new KeyStore.PrivateKeyEntry(privateKey, certChain), * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) + * .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + * .build()); + * // Key pair imported, obtain a reference to it. + * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null); + * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey(); + * // The original private key can now be discarded. + * + * Signature signature = Signature.getInstance("SHA256withECDSA"); + * signature.initSign(keyStorePrivateKey); + * ... + * }</pre> + * + * <p><h3>Example: RSA key pair for signing/verification using PKCS#1 padding</h3> + * This example illustrates how to import an RSA key pair into the Android KeyStore under alias + * {@code key2} with the private key authorized to be used only for signing using the PKCS#1 + * signature padding scheme with SHA-256 digest and only if the user has been authenticated within + * the last ten minutes. The use of public key is unrestricted, thus permitting signature + * verification using any padding schemes and digests, and without user authentication. Both the + * private and the public key must export their key material via {@link Key#getEncoded()} in + * {@code PKCS#8} and {@code X.509} format respectively. + * <pre> {@code + * PrivateKey privateKey = ...; // RSA private key + * Certificate[] certChain = ...; // Certificate chain with the first certificate + * // containing the corresponding RSA public key. + * + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * keyStore.setEntry( + * "key2", + * new KeyStore.PrivateKeyEntry(privateKey, certChain), + * new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) * .setDigests(KeyProperties.DIGEST_SHA256) + * .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) * // Only permit this key to be used if the user * // authenticated within the last ten minutes. * .setUserAuthenticationRequired(true) @@ -100,7 +166,40 @@ import javax.crypto.Mac; * // Key pair imported, obtain a reference to it. * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null); * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey(); - * // The original private key can now be thrown away. + * // The original private key can now be discarded. + * + * Signature signature = Signature.getInstance("SHA256withRSA"); + * signature.initSign(keyStorePrivateKey); + * ... + * }</pre> + * + * <p><h3>Example: RSA key pair for encryption/decryption using PKCS#1 padding</h3> + * This example illustrates how to import an RSA key pair into the Android KeyStore under alias + * {@code key2} with the private key authorized to be used only for decryption using the PKCS#1 + * encryption padding scheme. The use of public key is unrestricted, thus permitting encryption + * using any padding schemes and digests. Both the private and the public key must export their key + * material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} format respectively. + * <pre> {@code + * PrivateKey privateKey = ...; // RSA private key + * Certificate[] certChain = ...; // Certificate chain with the first certificate + * // containing the corresponding RSA public key. + * + * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + * keyStore.load(null); + * keyStore.setEntry( + * "key2", + * new KeyStore.PrivateKeyEntry(privateKey, certChain), + * new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) + * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) + * .build()); + * // Key pair imported, obtain a reference to it. + * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null); + * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey(); + * // The original private key can now be discarded. + * + * Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + * cipher.init(Cipher.DECRYPT_MODE, keyStorePrivateKey); + * ... * }</pre> */ public final class KeyProtection implements ProtectionParameter { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index e74334b..b9a9c24 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -148,7 +148,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { private WifiManager mWfm; private static String mWifiConfigFile; + // Chain of asynchronous operations used when rewriting the wifi supplicant config file + WifiDisableRunnable mWifiDisable = null; WifiRestoreRunnable mWifiRestore = null; + int mRetainedWifiState; // used only during config file rewrite // Class for capturing a network definition from the wifi supplicant config file static class Network { @@ -407,9 +410,47 @@ public class SettingsBackupAgent extends BackupAgentHelper { writeNewChecksums(stateChecksums, newState); } + class WifiDisableRunnable implements Runnable { + final WifiRestoreRunnable mNextPhase; + + public WifiDisableRunnable(WifiRestoreRunnable next) { + mNextPhase = next; + } + + @Override + public void run() { + if (DEBUG_BACKUP) { + Log.v(TAG, "Disabling wifi during restore"); + } + final ContentResolver cr = getContentResolver(); + final int scanAlways = Settings.Global.getInt(cr, + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); + final int retainedWifiState = enableWifi(false); + if (scanAlways != 0) { + Settings.Global.putInt(cr, + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); + } + + // Tell the final stage how to clean up after itself + mNextPhase.setPriorState(retainedWifiState, scanAlways); + + // And run it after a modest pause to give broadcasts and content + // observers time an opportunity to run on this looper thread, so + // that the wifi stack actually goes all the way down. + new Handler(getMainLooper()).postDelayed(mNextPhase, 2500); + } + } + class WifiRestoreRunnable implements Runnable { private byte[] restoredSupplicantData; private byte[] restoredWifiConfigFile; + private int retainedWifiState; // provided by disable stage + private int scanAlways; // provided by disable stage + + void setPriorState(int retainedState, int always) { + retainedWifiState = retainedState; + scanAlways = always; + } void incorporateWifiSupplicant(BackupDataInput data) { restoredSupplicantData = new byte[data.getDataSize()]; @@ -437,20 +478,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { public void run() { if (restoredSupplicantData != null || restoredWifiConfigFile != null) { if (DEBUG_BACKUP) { - Log.v(TAG, "Starting deferred restore of wifi data"); - } - final ContentResolver cr = getContentResolver(); - final int scanAlways = Settings.Global.getInt(cr, - Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); - final int retainedWifiState = enableWifi(false); - if (scanAlways != 0) { - Settings.Global.putInt(cr, - Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); + Log.v(TAG, "Applying restored wifi data"); } - // !!! Give the wifi stack a moment to quiesce. We've observed the - // response to disabling WIFI_SCAN_ALWAYS_AVAILABLE taking more - // than 1500ms, so we wait a generous 2500 here before proceeding. - try { Thread.sleep(2500); } catch (InterruptedException e) {} if (restoredSupplicantData != null) { restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, restoredSupplicantData, restoredSupplicantData.length); @@ -465,7 +494,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // restore the previous WIFI state. if (scanAlways != 0) { - Settings.Global.putInt(cr, + Settings.Global.putInt(getContentResolver(), Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways); } enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED || @@ -479,6 +508,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { void initWifiRestoreIfNecessary() { if (mWifiRestore == null) { mWifiRestore = new WifiRestoreRunnable(); + mWifiDisable = new WifiDisableRunnable(mWifiRestore); } } @@ -518,13 +548,16 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // If we have wifi data to restore, post a runnable to perform the - // bounce-and-update operation a little ways in the future. + // bounce-and-update operation a little ways in the future. The + // 'disable' runnable brings down the stack and remembers its state, + // and in turn schedules the 'restore' runnable to do the rewrite + // and cleanup operations. if (mWifiRestore != null) { long wifiBounceDelayMillis = Settings.Global.getLong( getContentResolver(), Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS, WIFI_BOUNCE_DELAY_MILLIS); - new Handler(getMainLooper()).postDelayed(mWifiRestore, wifiBounceDelayMillis); + new Handler(getMainLooper()).postDelayed(mWifiDisable, wifiBounceDelayMillis); } } diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png Binary files differnew file mode 100644 index 0000000..165ef4f --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png Binary files differindex d6e2065..f95f09f 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png Binary files differindex 6be4161..860a906 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png Binary files differindex b031273..bab268e 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png Binary files differnew file mode 100644 index 0000000..0feb405 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png Binary files differindex 12ceb90..cabab0d 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png Binary files differindex dc8809e..16e1bf5 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png Binary files differindex 5178ac5..40375de 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png Binary files differnew file mode 100644 index 0000000..b7b8f98 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png Binary files differindex 98be526..69b7449 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png Binary files differindex eed3f54..57d243c 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png Binary files differindex 22ae09d..e53eaff 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png Binary files differnew file mode 100644 index 0000000..695e7a4 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png Binary files differindex c819545..88294c0 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png Binary files differindex 6075caf..09d684a 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png Binary files differindex bccda1b..e31ea32 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png Binary files differnew file mode 100644 index 0000000..24f12d7 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png Binary files differnew file mode 100644 index 0000000..51482f5 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png Binary files differnew file mode 100644 index 0000000..46c7b18 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png Binary files differnew file mode 100644 index 0000000..396ad7d --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png 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 abdebf3..0068f84 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1121,9 +1121,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mViewPool.returnViewToPool(tv); } - // Notify the callback that we've removed the task and it can clean up after it - mCb.onTaskViewDismissed(removedTask); - // Get the stack scroll of the task to anchor to (since we are removing something, the front // most task will be our anchor task) Task anchorTask = null; @@ -1172,6 +1169,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Fade the dismiss button back in showDismissAllButton(); } + + // Notify the callback that we've removed the task and it can clean up after it. Note, we + // do this after onAllTaskViewsDismissed() is called, to allow the home activity to be + // started before the call to remove the task. + mCb.onTaskViewDismissed(removedTask); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 00fa653..7065343 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -41,6 +41,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -1713,10 +1714,16 @@ public abstract class BaseStatusBar extends SystemUI implements sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + final Icon smallIcon = n.getSmallIcon(); + if (smallIcon == null) { + handleNotificationError(sbn, + "No small icon in notification from " + sbn.getPackageName()); + return null; + } final StatusBarIcon ic = new StatusBarIcon( sbn.getUser(), sbn.getPackageName(), - n.getSmallIcon(), + smallIcon, n.iconLevel, n.number, n.tickerText); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 6627360..42a2f90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -29,6 +29,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; +import android.hardware.fingerprint.FingerprintManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; @@ -601,6 +602,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { post(new Runnable() { @Override @@ -671,7 +673,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onFingerprintError(int msgId, String errString) { - if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) { + if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed() + || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) { return; } // TODO: Go to bouncer if this is "too many attempts" (lockout) error. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 59cf2bf..059ecee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -258,7 +258,7 @@ public class NavigationBarView extends LinearLayout { mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back); mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land); mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime); - mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime); + mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime_land); mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent); mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index d3f33ab..50234b2 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -16,7 +16,6 @@ package com.android.systemui.tuner; import android.app.ActivityManager; -import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -25,10 +24,12 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; @@ -169,6 +170,7 @@ public class TunerService extends SystemUI { public static final void showResetRequest(final Context context, final Runnable onDisabled) { SystemUIDialog dialog = new SystemUIDialog(context); + dialog.setShowForAllUsers(true); dialog.setMessage(R.string.remove_from_settings_prompt); dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel), (OnClickListener) null); @@ -192,7 +194,7 @@ public class TunerService extends SystemUI { } public static final void setTunerEnabled(Context context, boolean enabled) { - context.getPackageManager().setComponentEnabledSetting( + userContext(context).getPackageManager().setComponentEnabledSetting( new ComponentName(context, TunerActivity.class), enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, @@ -200,11 +202,20 @@ public class TunerService extends SystemUI { } public static final boolean isTunerEnabled(Context context) { - return context.getPackageManager().getComponentEnabledSetting( + return userContext(context).getPackageManager().getComponentEnabledSetting( new ComponentName(context, TunerActivity.class)) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; } + private static Context userContext(Context context) { + try { + return context.createPackageContextAsUser(context.getPackageName(), 0, + new UserHandle(ActivityManager.getCurrentUser())); + } catch (NameNotFoundException e) { + return context; + } + } + private class Observer extends ContentObserver { public Observer() { super(new Handler(Looper.getMainLooper())); diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 30680ed..fa87270 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -2905,9 +2905,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } // Now that we've told the host, push out an update. sendUpdateIntentLocked(provider, appWidgetIds); - providersUpdated = true; } } + providersUpdated = true; } } } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index aab6374..8b0e6f2 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -283,7 +283,22 @@ public class AccountManagerService // Don't delete accounts when updating a authenticator's // package. if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - purgeOldGrantsAll(); + /* Purging data requires file io, don't block the main thread. This is probably + * less than ideal because we are introducing a race condition where old grants + * could be exercised until they are purged. But that race condition existed + * anyway with the broadcast receiver. + * + * Ideally, we would completely clear the cache, purge data from the database, + * and then rebuild the cache. All under the cache lock. But that change is too + * large at this point. + */ + Runnable r = new Runnable() { + @Override + public void run() { + purgeOldGrantsAll(); + } + }; + new Thread(r).start(); } } }, intentFilter); @@ -329,52 +344,6 @@ public class AccountManagerService return mUserManager; } - /* Caller should lock mUsers */ - private UserAccounts initUserLocked(int userId) { - UserAccounts accounts = mUsers.get(userId); - if (accounts == null) { - accounts = new UserAccounts(mContext, userId); - initializeDebugDbSizeAndCompileSqlStatementForLogging( - accounts.openHelper.getWritableDatabase(), accounts); - mUsers.append(userId, accounts); - purgeOldGrants(accounts); - validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); - } - return accounts; - } - - private void purgeOldGrantsAll() { - synchronized (mUsers) { - for (int i = 0; i < mUsers.size(); i++) { - purgeOldGrants(mUsers.valueAt(i)); - } - } - } - - private void purgeOldGrants(UserAccounts accounts) { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - final Cursor cursor = db.query(TABLE_GRANTS, - new String[]{GRANTS_GRANTEE_UID}, - null, null, GRANTS_GRANTEE_UID, null, null); - try { - while (cursor.moveToNext()) { - final int uid = cursor.getInt(0); - final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; - if (packageExists) { - continue; - } - Log.d(TAG, "deleting grants for UID " + uid - + " because its package is no longer installed"); - db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", - new String[]{Integer.toString(uid)}); - } - } finally { - cursor.close(); - } - } - } - /** * Validate internal set of accounts against installed authenticators for * given user. Clears cached authenticators before validating. @@ -469,13 +438,49 @@ public class AccountManagerService synchronized (mUsers) { UserAccounts accounts = mUsers.get(userId); if (accounts == null) { - accounts = initUserLocked(userId); + accounts = new UserAccounts(mContext, userId); + initializeDebugDbSizeAndCompileSqlStatementForLogging( + accounts.openHelper.getWritableDatabase(), accounts); mUsers.append(userId, accounts); + purgeOldGrants(accounts); + validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); } return accounts; } } + private void purgeOldGrantsAll() { + synchronized (mUsers) { + for (int i = 0; i < mUsers.size(); i++) { + purgeOldGrants(mUsers.valueAt(i)); + } + } + } + + private void purgeOldGrants(UserAccounts accounts) { + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); + final Cursor cursor = db.query(TABLE_GRANTS, + new String[]{GRANTS_GRANTEE_UID}, + null, null, GRANTS_GRANTEE_UID, null, null); + try { + while (cursor.moveToNext()) { + final int uid = cursor.getInt(0); + final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; + if (packageExists) { + continue; + } + Log.d(TAG, "deleting grants for UID " + uid + + " because its package is no longer installed"); + db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", + new String[]{Integer.toString(uid)}); + } + } finally { + cursor.close(); + } + } + } + private void onUserRemoved(Intent intent) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userId < 1) return; @@ -2510,8 +2515,9 @@ public class AccountManagerService } long identityToken = clearCallingIdentity(); try { + UserAccounts accounts = getUserAccounts(userId); return getAccountsInternal( - userId, + accounts, callingUid, null, // packageName visibleAccountTypes); @@ -2609,8 +2615,9 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { + UserAccounts accounts = getUserAccounts(userId); return getAccountsInternal( - userId, + accounts, callingUid, callingPackage, visibleAccountTypes); @@ -2620,13 +2627,11 @@ public class AccountManagerService } private Account[] getAccountsInternal( - int userId, + UserAccounts userAccounts, int callingUid, String callingPackage, List<String> visibleAccountTypes) { - UserAccounts accounts = getUserAccounts(userId); - synchronized (accounts.cacheLock) { - UserAccounts userAccounts = getUserAccounts(userId); + synchronized (userAccounts.cacheLock) { ArrayList<Account> visibleAccounts = new ArrayList<>(); for (String visibleType : visibleAccountTypes) { Account[] accountsForType = getAccountsFromCacheLocked( diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index a75cc48..6e34876 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3740,7 +3740,12 @@ final class ActivityStack { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId); boolean prevIsHome = false; - if (tr.isOverHomeStack()) { + + // If true, we should resume the home activity next if the task we are moving to the + // back is over the home stack. We force to false if the task we are moving to back + // is the home task and we don't want it resumed after moving to the back. + final boolean canGoHome = !tr.isHomeTask() && tr.isOverHomeStack(); + if (canGoHome) { final TaskRecord nextTask = getNextTask(tr); if (nextTask != null) { nextTask.setTaskToReturnTo(tr.getTaskToReturnTo()); @@ -3774,8 +3779,7 @@ final class ActivityStack { } final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null; - if (prevIsHome || task == tr && tr.isOverHomeStack() - || numTasks <= 1 && isOnHomeDisplay()) { + if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! return false; diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index c705fbf..2c9d82b 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -454,6 +454,18 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe "Must have " + permission + " permission."); } + int getEffectiveUserId(int userId) { + UserManager um = UserManager.get(mContext); + if (um != null) { + final long callingIdentity = Binder.clearCallingIdentity(); + userId = um.getCredentialOwnerProfile(userId); + Binder.restoreCallingIdentity(callingIdentity); + } else { + Slog.e(TAG, "Unable to acquire UserManager"); + } + return userId; + } + boolean isCurrentUserOrProfile(int userId) { UserManager um = UserManager.get(mContext); @@ -686,11 +698,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length); + // Group ID is arbitrarily set to parent profile user ID. It just represents + // the default fingerprints for the user. + final int effectiveGroupId = getEffectiveUserId(groupId); + final boolean restricted = isRestricted(); mHandler.post(new Runnable() { @Override public void run() { - startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted); + startEnrollment(token, cryptoClone, effectiveGroupId, receiver, flags, restricted); } }); } @@ -724,11 +740,16 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe Slog.w(TAG, "Calling not granted permission to use fingerprint"); return; } + + // Group ID is arbitrarily set to parent profile user ID. It just represents + // the default fingerprints for the user. + final int effectiveGroupId = getEffectiveUserId(groupId); + final boolean restricted = isRestricted(); mHandler.post(new Runnable() { @Override public void run() { - startAuthentication(token, opId, groupId, receiver, flags, restricted); + startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted); } }); } @@ -751,10 +772,14 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe final IFingerprintServiceReceiver receiver) { checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission final boolean restricted = isRestricted(); + + // Group ID is arbitrarily set to parent profile user ID. It just represents + // the default fingerprints for the user. + final int effectiveGroupId = getEffectiveUserId(groupId); mHandler.post(new Runnable() { @Override public void run() { - startRemove(token, fingerId, groupId, receiver, restricted); + startRemove(token, fingerId, effectiveGroupId, receiver, restricted); } }); @@ -771,10 +796,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe @Override // Binder call public void rename(final int fingerId, final int groupId, final String name) { checkPermission(MANAGE_FINGERPRINT); + + // Group ID is arbitrarily set to parent profile user ID. It just represents + // the default fingerprints for the user. + final int effectiveGroupId = getEffectiveUserId(groupId); mHandler.post(new Runnable() { @Override public void run() { - mFingerprintUtils.renameFingerprintForUser(mContext, fingerId, groupId, name); + mFingerprintUtils.renameFingerprintForUser(mContext, fingerId, + effectiveGroupId, name); } }); } @@ -784,15 +814,19 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe if (!canUseFingerprint(opPackageName)) { return Collections.emptyList(); } - return FingerprintService.this.getEnrolledFingerprints(userId); + int effectiveUserId = getEffectiveUserId(userId); + + return FingerprintService.this.getEnrolledFingerprints(effectiveUserId); } @Override // Binder call - public boolean hasEnrolledFingerprints(int groupId, String opPackageName) { + public boolean hasEnrolledFingerprints(int userId, String opPackageName) { if (!canUseFingerprint(opPackageName)) { return false; } - return FingerprintService.this.hasEnrolledFingerprints(groupId); + + int effectiveUserId = getEffectiveUserId(userId); + return FingerprintService.this.hasEnrolledFingerprints(effectiveUserId); } @Override // Binder call @@ -829,8 +863,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe IFingerprintDaemon daemon = getFingerprintDaemon(); if (daemon != null) { try { - // TODO: if this is a managed profile, use the profile parent's directory for - // storage. + userId = getEffectiveUserId(userId); final File systemDir = Environment.getUserSystemDirectory(userId); final File fpDir = new File(systemDir, FP_DATA_DIR); if (!fpDir.exists()) { diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 8e2ca18..92df851 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -108,6 +108,7 @@ public class IdleController extends StateController { private AlarmManager mAlarm; private PendingIntent mIdleTriggerIntent; boolean mIdle; + boolean mScreenOn; public IdlenessTracker() { mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); @@ -120,6 +121,7 @@ public class IdleController extends StateController { // At boot we presume that the user has just "interacted" with the // device in some meaningful way. mIdle = false; + mScreenOn = true; } public boolean isIdle() { @@ -149,12 +151,14 @@ public class IdleController extends StateController { if (action.equals(Intent.ACTION_SCREEN_ON) || action.equals(Intent.ACTION_DREAMING_STOPPED)) { - // possible transition to not-idle + if (DEBUG) { + Slog.v(TAG,"exiting idle : " + action); + } + mScreenOn = true; + //cancel the alarm + mAlarm.cancel(mIdleTriggerIntent); if (mIdle) { - if (DEBUG) { - Slog.v(TAG, "exiting idle : " + action); - } - mAlarm.cancel(mIdleTriggerIntent); + // possible transition to not-idle mIdle = false; reportNewIdleState(mIdle); } @@ -169,11 +173,12 @@ public class IdleController extends StateController { Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" + when); } + mScreenOn = false; mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, IDLE_WINDOW_SLOP, mIdleTriggerIntent); } else if (action.equals(ACTION_TRIGGER_IDLE)) { - // idle time starts now - if (!mIdle) { + // idle time starts now. Do not set mIdle if screen is on. + if (!mIdle && !mScreenOn) { if (DEBUG) { Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6a4ae3d..21e256e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -481,12 +481,21 @@ public class PackageManagerService extends IPackageManager.Stub { new ArrayMap<String, ArrayMap<String, PackageParser.Package>>(); /** - * Tracks new system packages [receiving in an OTA] that we expect to + * Tracks new system packages [received in an OTA] that we expect to * find updated user-installed versions. Keys are package name, values * are package location. */ final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); + /** + * Tracks existing system packages prior to receiving an OTA. Keys are package name. + */ + final private ArraySet<String> mExistingSystemPackages = new ArraySet<>(); + /** + * Whether or not system app permissions should be promoted from install to runtime. + */ + boolean mPromoteSystemApps; + final Settings mSettings; boolean mRestoredSettings; @@ -2028,6 +2037,24 @@ public class PackageManagerService extends IPackageManager.Stub { } } + final VersionInfo ver = mSettings.getInternalVersion(); + mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + // when upgrading from pre-M, promote system app permissions from install to runtime + mPromoteSystemApps = + mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; + + // save off the names of pre-existing system packages prior to scanning; we don't + // want to automatically grant runtime permissions for new system apps + if (mPromoteSystemApps) { + Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator(); + while (pkgSettingIter.hasNext()) { + PackageSetting ps = pkgSettingIter.next(); + if (isSystemApp(ps)) { + mExistingSystemPackages.add(ps.name); + } + } + } + // Collect vendor overlay packages. // (Do this before scanning any apps.) // For security and version matching reason, only consider @@ -2247,8 +2274,6 @@ public class PackageManagerService extends IPackageManager.Stub { // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. - final VersionInfo ver = mSettings.getInternalVersion(); - int updateFlags = UPDATE_PERMISSIONS_ALL; if (ver.sdkVersion != mSdkVersion) { Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to " @@ -2257,6 +2282,9 @@ public class PackageManagerService extends IPackageManager.Stub { } updatePermissionsLPw(null, null, updateFlags); ver.sdkVersion = mSdkVersion; + // clear only after permissions have been updated + mExistingSystemPackages.clear(); + mPromoteSystemApps = false; // If this is the first boot, and it is a normal boot, then // we need to initialize the default preferred apps. @@ -2268,7 +2296,6 @@ public class PackageManagerService extends IPackageManager.Stub { // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. - mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (int i = 0; i < mSettings.mPackages.size(); i++) { @@ -8356,6 +8383,13 @@ public class PackageManagerService extends IPackageManager.Stub { } else if (origPermissions.hasInstallPermission(bp.name)) { // For legacy apps that became modern, install becomes runtime. grant = GRANT_UPGRADE; + } else if (mPromoteSystemApps + && isSystemApp(ps) + && mExistingSystemPackages.contains(ps.name)) { + // For legacy system apps, install becomes runtime. + // We cannot check hasInstallPermission() for system apps since those + // permissions were granted implicitly and not persisted pre-M. + grant = GRANT_UPGRADE; } else { // For modern apps keep runtime permissions unchanged. grant = GRANT_RUNTIME; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c4ff277..2a79a47 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3617,7 +3617,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // We currently want to hide the navigation UI. mNavigationBarController.setBarShowingLw(false); } - if (navVisible && !navTranslucent && !mNavigationBar.isAnimatingLw() + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the nav bar is currently requested to be visible, // and not in the process of animating on or off, then diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java index 147efdd..c71b48f 100644 --- a/services/core/java/com/android/server/policy/WindowOrientationListener.java +++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java @@ -465,6 +465,9 @@ public abstract class WindowOrientationListener { pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); pw.println(prefix + "mLastFilteredY=" + mLastFilteredY); pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ); + final long delta = SystemClock.elapsedRealtimeNanos() - mLastFilteredTimestampNanos; + pw.println(prefix + "mLastFilteredTimestampNanos=" + mLastFilteredTimestampNanos + + " (" + (delta * 0.000001f) + "ms ago)"); pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}"); pw.println(prefix + "mFlat=" + mFlat); pw.println(prefix + "mSwinging=" + mSwinging); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5d6df26..4db0b1e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -203,7 +203,9 @@ class DisplayContent { } void moveStack(TaskStack stack, boolean toTop) { - mStacks.remove(stack); + if (!mStacks.remove(stack)) { + Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable()); + } mStacks.add(toTop ? mStacks.size() : 0, stack); } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 4545032..794b49c 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -385,8 +385,6 @@ public class TaskStack { } close(); - - mDisplayContent = null; } void resetAnimationBackgroundAnimator() { @@ -518,6 +516,7 @@ public class TaskStack { mDimLayer.destroySurface(); mDimLayer = null; } + mDisplayContent = null; } public void dump(String prefix, PrintWriter pw) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 014527b..bd72860 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -95,6 +95,7 @@ import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; import java.io.File; +import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -182,6 +183,23 @@ public final class SystemServer { SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); } + // If the system has "persist.sys.language" and friends set, replace them with + // "persist.sys.locale". Note that the default locale at this point is calculated + // using the "-Duser.locale" command line flag. That flag is usually populated by + // AndroidRuntime using the same set of system properties, but only the system_server + // and system apps are allowed to set them. + // + // NOTE: Most changes made here will need an equivalent change to + // core/jni/AndroidRuntime.cpp + if (!SystemProperties.get("persist.sys.language").isEmpty()) { + final String languageTag = Locale.getDefault().toLanguageTag(); + + SystemProperties.set("persist.sys.locale", languageTag); + SystemProperties.set("persist.sys.language", ""); + SystemProperties.set("persist.sys.country", ""); + SystemProperties.set("persist.sys.localevar", ""); + } + // Here we go! Slog.i(TAG, "Entered the Android system server!"); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis()); @@ -1024,12 +1042,6 @@ public final class SystemServer { w.getDefaultDisplay().getMetrics(metrics); context.getResources().updateConfiguration(config, metrics); - // The system context's theme may be configuration-dependent. - final Theme systemTheme = context.getTheme(); - if (systemTheme.getChangingConfigurations() != 0) { - systemTheme.rebase(); - } - try { // TODO: use boot phase mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService()); diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index 2d40291..9ee9cf4 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -603,7 +603,10 @@ public class DhcpClient extends BaseDhcpStateMachine { @Override public void exit() { cancelOneshotTimeout(); - mReceiveThread.halt(); // Also closes sockets. + if (mReceiveThread != null) { + mReceiveThread.halt(); // Also closes sockets. + mReceiveThread = null; + } clearDhcpState(); } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 303a492..80515cf 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -220,6 +220,8 @@ public class ServiceState implements Parcelable { private int mCdmaEriIconIndex; private int mCdmaEriIconMode; + private boolean mIsDataRoamingFromRegistration; + /** * get String description of roaming type * @hide @@ -297,6 +299,7 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = s.mCdmaEriIconIndex; mCdmaEriIconMode = s.mCdmaEriIconMode; mIsEmergencyOnly = s.mIsEmergencyOnly; + mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration; } /** @@ -324,6 +327,7 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = in.readInt(); mCdmaEriIconMode = in.readInt(); mIsEmergencyOnly = in.readInt() != 0; + mIsDataRoamingFromRegistration = in.readInt() != 0; } public void writeToParcel(Parcel out, int flags) { @@ -348,6 +352,7 @@ public class ServiceState implements Parcelable { out.writeInt(mCdmaEriIconIndex); out.writeInt(mCdmaEriIconMode); out.writeInt(mIsEmergencyOnly ? 1 : 0); + out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0); } public int describeContents() { @@ -439,6 +444,26 @@ public class ServiceState implements Parcelable { } /** + * Set whether data network registration state is roaming + * + * This should only be set to the roaming value received + * once the data registration phase has completed. + * @hide + */ + public void setDataRoamingFromRegistration(boolean dataRoaming) { + mIsDataRoamingFromRegistration = dataRoaming; + } + + /** + * Get whether data network registration state is roaming + * @return true if registration indicates roaming, false otherwise + * @hide + */ + public boolean getDataRoamingFromRegistration() { + return mIsDataRoamingFromRegistration; + } + + /** * Get current data network roaming type * @return roaming type * @hide @@ -599,7 +624,8 @@ public class ServiceState implements Parcelable { + ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode()) + mCdmaRoamingIndicator + mCdmaDefaultRoamingIndicator - + (mIsEmergencyOnly ? 1 : 0)); + + (mIsEmergencyOnly ? 1 : 0) + + (mIsDataRoamingFromRegistration ? 1 : 0)); } @Override @@ -635,7 +661,8 @@ public class ServiceState implements Parcelable { && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator) && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, s.mCdmaDefaultRoamingIndicator) - && mIsEmergencyOnly == s.mIsEmergencyOnly); + && mIsEmergencyOnly == s.mIsEmergencyOnly + && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration); } /** @@ -736,7 +763,8 @@ public class ServiceState implements Parcelable { + " " + mSystemId + " RoamInd=" + mCdmaRoamingIndicator + " DefRoamInd=" + mCdmaDefaultRoamingIndicator - + " EmergOnly=" + mIsEmergencyOnly); + + " EmergOnly=" + mIsEmergencyOnly + + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration); } private void setNullState(int state) { @@ -762,6 +790,7 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = -1; mCdmaEriIconMode = -1; mIsEmergencyOnly = false; + mIsDataRoamingFromRegistration = false; } public void setStateOutOfService() { @@ -934,6 +963,7 @@ public class ServiceState implements Parcelable { mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator"); mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator"); mIsEmergencyOnly = m.getBoolean("emergencyOnly"); + mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration"); } /** @@ -962,6 +992,7 @@ public class ServiceState implements Parcelable { m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator); m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator); m.putBoolean("emergencyOnly", Boolean.valueOf(mIsEmergencyOnly)); + m.putBoolean("isDataRoamingFromRegistration", Boolean.valueOf(mIsDataRoamingFromRegistration)); } /** @hide */ diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index 73cb432..7b9c9f1 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -24,6 +24,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-sdk android:targetSdkVersion="22" /> <application android:label="ActivityTest"> <activity android:name="ActivityTestMain"> diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 2f0bf39..5fbfe8a 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -465,9 +465,12 @@ public class ActivityTestMain extends Activity { menu.add("Ignore battery optimizations").setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + Intent intent; if (!mPower.isIgnoringBatteryOptimizations(getPackageName())) { + intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.fromParts("package", getPackageName(), null)); + } else { + intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); } startActivity(intent); return true; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index 47258b6..baf2e2e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -97,13 +97,13 @@ public final class DelegateManager<T> { * @return the delegate or null if not found. */ @Nullable - public T getDelegate(long native_object) { + public synchronized T getDelegate(long native_object) { if (native_object > 0) { - T delegate = mDelegates.get(native_object); + T delegate = mDelegates.get(native_object); if (Debug.DEBUG) { if (delegate == null) { - System.out.println("Unknown " + mClass.getSimpleName() + " with int " + + System.err.println("Unknown " + mClass.getSimpleName() + " with int " + native_object); } } @@ -119,14 +119,18 @@ public final class DelegateManager<T> { * @param newDelegate the delegate to add * @return a unique native int to identify the delegate */ - public long addNewDelegate(T newDelegate) { + public synchronized long addNewDelegate(T newDelegate) { long native_object = ++mDelegateCounter; + mDelegates.put(native_object, newDelegate); assert !mJavaReferences.contains(newDelegate); mJavaReferences.add(newDelegate); if (Debug.DEBUG) { - System.out.println("New " + mClass.getSimpleName() + " with int " + native_object); + System.out.println( + "New " + mClass.getSimpleName() + " " + + "with int " + + native_object); } return native_object; @@ -136,7 +140,7 @@ public final class DelegateManager<T> { * Removes the main reference on the given delegate. * @param native_object the native integer representing the delegate. */ - public void removeJavaReferenceFor(long native_object) { + public synchronized void removeJavaReferenceFor(long native_object) { T delegate = getDelegate(native_object); if (Debug.DEBUG) { |