diff options
39 files changed, 915 insertions, 599 deletions
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5e7bd0d..9ea1606 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -142,13 +142,22 @@ public class AlarmManager { public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; /** + * Flag for alarms: same as {@link #FLAG_ALLOW_WHILE_IDLE}, but doesn't have restrictions + * on how frequently it can be scheduled. Only available (and automatically applied) to + * system alarms. + * + * @hide + */ + public static final int FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED = 1<<3; + + /** * Flag for alarms: this alarm marks the point where we would like to come out of idle * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm. * Scheduling an alarm with this flag puts the alarm manager in to idle mode, where it * avoids scheduling any further alarms until the marker alarm is executed. * @hide */ - public static final int FLAG_IDLE_UNTIL = 1<<3; + public static final int FLAG_IDLE_UNTIL = 1<<4; private final IAlarmManager mService; private final boolean mAlwaysExact; @@ -565,6 +574,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * + * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off + * for a particular application. Under normal system operation, it will not dispatch these + * alarms more than about every minute (at which point every such pending alarm is + * dispatched); when in low-power idle modes this duration may be significantly longer, + * such as 15 minutes.</p> + * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms @@ -608,6 +623,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * + * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off + * for a particular application. Under normal system operation, it will not dispatch these + * alarms more than about every minute (at which point every such pending alarm is + * dispatched); when in low-power idle modes this duration may be significantly longer, + * such as 15 minutes.</p> + * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 96c6878..33a47b2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1331,11 +1331,14 @@ public class Notification implements Parcelable public Notification(Context context, int icon, CharSequence tickerText, long when, CharSequence contentTitle, CharSequence contentText, Intent contentIntent) { - this.when = when; - this.icon = icon; - this.tickerText = tickerText; - setLatestEventInfo(context, contentTitle, contentText, - PendingIntent.getActivity(context, 0, contentIntent, 0)); + new Builder(context) + .setWhen(when) + .setSmallIcon(icon) + .setTicker(tickerText) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0)) + .buildInto(this); } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 42d0dcb..9f49154 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -149,6 +149,7 @@ public class DevicePolicyManager { * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li> * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li> * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li> + * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li> * </ul> * * <p> When device owner provisioning has completed, an intent of the type @@ -163,14 +164,19 @@ public class DevicePolicyManager { = "android.app.action.PROVISION_MANAGED_DEVICE"; /** - * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that allows - * a mobile device management application that starts managed profile provisioning to pass data - * to itself on the managed profile when provisioning completes. The mobile device management - * application sends this extra in an intent with the action - * {@link #ACTION_PROVISION_MANAGED_PROFILE} and receives it in + * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that + * allows a mobile device management application which starts managed provisioning to pass data + * to itself. + * <p> + * If used with {@link #ACTION_PROVISION_MANAGED_PROFILE} it can be used by the application that + * sends the intent to pass data to itself on the newly created profile. + * If used with {@link #ACTION_PROVISION_MANAGED_DEVICE} it allows passing data to the same + * instance of the app on the primary user. + * <p> + * In both cases the application receives the data in * {@link DeviceAdminReceiver#onProfileProvisioningComplete} via an intent with the action * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}. The bundle is not changed - * during the managed profile provisioning. + * during the managed provisioning. */ public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index f7d2821..353388d 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -246,41 +246,65 @@ public class TimeUtils { public static final long NANOS_PER_MS = 1000000; private static final Object sFormatSync = new Object(); - private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; - - private static final long LARGEST_DURATION = (1000 * DateUtils.DAY_IN_MILLIS) - 1; + private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; + private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; static private int accumField(int amt, int suffix, boolean always, int zeropad) { - if (amt > 99 || (always && zeropad >= 3)) { - return 3+suffix; - } - if (amt > 9 || (always && zeropad >= 2)) { - return 2+suffix; - } - if (always || amt > 0) { - return 1+suffix; + if (amt > 999) { + int num = 0; + while (amt != 0) { + num++; + amt /= 10; + } + return num + suffix; + } else { + if (amt > 99 || (always && zeropad >= 3)) { + return 3+suffix; + } + if (amt > 9 || (always && zeropad >= 2)) { + return 2+suffix; + } + if (always || amt > 0) { + return 1+suffix; + } } return 0; } - static private int printField(char[] formatStr, int amt, char suffix, int pos, + static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, boolean always, int zeropad) { if (always || amt > 0) { final int startPos = pos; - if ((always && zeropad >= 3) || amt > 99) { - int dig = amt/100; - formatStr[pos] = (char)(dig + '0'); - pos++; - amt -= (dig*100); - } - if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { - int dig = amt/10; - formatStr[pos] = (char)(dig + '0'); + if (amt > 999) { + int tmp = 0; + while (amt != 0 && tmp < sTmpFormatStr.length) { + int dig = amt % 10; + sTmpFormatStr[tmp] = (char)(dig + '0'); + tmp++; + amt /= 10; + } + tmp--; + while (tmp >= 0) { + formatStr[pos] = sTmpFormatStr[tmp]; + pos++; + tmp--; + } + } else { + if ((always && zeropad >= 3) || amt > 99) { + int dig = amt/100; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*100); + } + if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { + int dig = amt/10; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*10); + } + formatStr[pos] = (char)(amt + '0'); pos++; - amt -= (dig*10); } - formatStr[pos] = (char)(amt + '0'); - pos++; formatStr[pos] = suffix; pos++; } @@ -312,10 +336,6 @@ public class TimeUtils { duration = -duration; } - if (duration > LARGEST_DURATION) { - duration = LARGEST_DURATION; - } - int millis = (int)(duration%1000); int seconds = (int) Math.floor(duration / 1000); int days = 0, hours = 0, minutes = 0; @@ -353,11 +373,11 @@ public class TimeUtils { int start = pos; boolean zeropad = fieldLen != 0; - pos = printField(formatStr, days, 'd', pos, false, 0); - pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); - pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); - pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); - pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); + pos = printFieldLocked(formatStr, days, 'd', pos, false, 0); + pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); formatStr[pos] = 's'; return pos + 1; } diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java index 52ea1c4..4cb617e 100644 --- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java +++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java @@ -77,15 +77,20 @@ public class NotificationStressTest extends InstrumentationTestCase { } private void sendNotification(int id, CharSequence text) { - // Create "typical" notification with random icon - Notification notification = new Notification(ICONS[mRandom.nextInt(ICONS.length)], text, - System.currentTimeMillis()); // Fill in arbitrary content Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0); CharSequence title = text + " " + id; CharSequence subtitle = String.valueOf(System.currentTimeMillis()); - notification.setLatestEventInfo(mContext, title, subtitle, pendingIntent); + // Create "typical" notification with random icon + Notification notification = new Notification.Builder(mContext) + .setSmallIcon(ICONS[mRandom.nextInt(ICONS.length)]) + .setTicker(text) + .setWhen(System.currentTimeMillis()) + .setContentTitle(title) + .setContentText(subtitle) + .setContentIntent(pendingIntent) + .build(); mNotificationManager.notify(id, notification); SystemClock.sleep(10); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index 19375a2..d2d5850 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -368,7 +368,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor byte[] output; try { - output = mMainDataStreamer.doFinal(input, inputOffset, inputLen); + byte[] additionalEntropy = + KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( + mRng, getAdditionalEntropyAmountForFinish()); + output = mMainDataStreamer.doFinal(input, inputOffset, inputLen, additionalEntropy); } catch (KeyStoreException e) { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH: @@ -667,21 +670,37 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor /** * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's - * {@code begin} operation. + * {@code begin} operation. This amount of entropy is typically what's consumed to generate + * random parameters, such as IV. * - * <p>For decryption, this should be {@code 0} because decryption should not be consuming any - * entropy. For encryption, this value should match (or exceed) the amount of Shannon entropy of - * the ciphertext produced by this cipher assuming the key, the plaintext, and all explicitly - * provided parameters to {@code Cipher.init} are known. For example, for AES CBC encryption - * with an explicitly provided IV this should be {@code 0}, whereas for the case where IV is - * generated by the KeyStore's {@code begin} operation this should be {@code 16}. For RSA with - * OAEP this should be the size of the OAEP hash output. For RSA with PKCS#1 padding this should - * be the size of the padding string or could be raised (for simplicity) to the size of the - * modulus. + * <p>For decryption, the return value should be {@code 0} because decryption should not be + * consuming any entropy. For encryption, the value combined with + * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon + * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all + * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC + * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for + * the case where IV is generated by the KeyStore's {@code begin} operation it should be + * {@code 16}. */ protected abstract int getAdditionalEntropyAmountForBegin(); /** + * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's + * {@code finish} operation. This amount of entropy is typically what's consumed by encryption + * padding scheme. + * + * <p>For decryption, the return value should be {@code 0} because decryption should not be + * consuming any entropy. For encryption, the value combined with + * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon + * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all + * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with + * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding + * the return value should be the size of the padding string or could be raised (for simplicity) + * to the size of the modulus. + */ + protected abstract int getAdditionalEntropyAmountForFinish(); + + /** * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. * * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java index 335da07..d19a766 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java @@ -117,7 +117,7 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature } @Override - protected int getAdditionalEntropyAmountForBegin() { - return (isSigning()) ? mGroupSizeBytes : 0; + protected int getAdditionalEntropyAmountForSign() { + return mGroupSizeBytes; } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java index f31c06d..f7c184c 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java @@ -232,7 +232,10 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC byte[] result; try { - result = mChunkedStreamer.doFinal(null, 0, 0); + result = mChunkedStreamer.doFinal( + null, 0, 0, + null // no additional entropy needed -- HMAC is deterministic + ); } catch (KeyStoreException e) { throw new ProviderException("Keystore operation failed", e); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 35af34f..b93424d 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -215,14 +215,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato legacySpec.getKeystoreAlias(), KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); + // Authorized to be used with any digest (including no digest). + specBuilder.setDigests(KeyProperties.DIGEST_NONE); break; case KeymasterDefs.KM_ALGORITHM_RSA: specBuilder = new KeyGenParameterSpec.Builder( @@ -231,19 +225,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato | KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); + // Authorized to be used with any digest (including no digest). + specBuilder.setDigests(KeyProperties.DIGEST_NONE); specBuilder.setSignaturePaddings( KeyProperties.SIGNATURE_PADDING_RSA_PKCS1); + // Authorized to be used with any padding (including no padding). specBuilder.setEncryptionPaddings( - KeyProperties.ENCRYPTION_PADDING_NONE, - KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1); + KeyProperties.ENCRYPTION_PADDING_NONE); // Disable randomized encryption requirement to support encryption // padding NONE above. specBuilder.setRandomizedEncryptionRequired(false); @@ -703,6 +691,36 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } case KeymasterDefs.KM_ALGORITHM_RSA: { + // Check whether this key is authorized for PKCS#1 signature padding. + // We use Bouncy Castle to generate self-signed RSA certificates. Bouncy Castle + // only supports RSA certificates signed using PKCS#1 padding scheme. The key needs + // to be authorized for PKCS#1 padding or padding NONE which means any padding. + boolean pkcs1SignaturePaddingSupported = false; + for (int keymasterPadding : KeyProperties.SignaturePadding.allToKeymaster( + spec.getSignaturePaddings())) { + if ((keymasterPadding == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN) + || (keymasterPadding == KeymasterDefs.KM_PAD_NONE)) { + pkcs1SignaturePaddingSupported = true; + break; + } + } + if (!pkcs1SignaturePaddingSupported) { + // Keymaster doesn't distinguish between encryption padding NONE and signature + // padding NONE. In the Android Keystore API only encryption padding NONE is + // exposed. + for (int keymasterPadding : KeyProperties.EncryptionPadding.allToKeymaster( + spec.getEncryptionPaddings())) { + if (keymasterPadding == KeymasterDefs.KM_PAD_NONE) { + pkcs1SignaturePaddingSupported = true; + break; + } + } + } + if (!pkcs1SignaturePaddingSupported) { + // Key not authorized for PKCS#1 signature padding -- can't sign + return null; + } + Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests( spec.getDigests(), AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests()); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java index d33692a..6abdf19 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java @@ -99,6 +99,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } @Override + protected final int getAdditionalEntropyAmountForFinish() { + return 0; + } + + @Override @NonNull protected KeyStoreCryptoOperationStreamer createMainDataStreamer( KeyStore keyStore, IBinder operationToken) { @@ -142,7 +147,8 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } @Override - public byte[] doFinal(byte[] input, int inputOffset, int inputLength) + public byte[] doFinal(byte[] input, int inputOffset, int inputLength, + byte[] additionalEntropy) throws KeyStoreException { if (inputLength > 0) { mInputBuffer.write(input, inputOffset, inputLength); @@ -165,7 +171,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase "Message size (" + bufferedInput.length + " bytes) must be smaller than" + " modulus (" + mModulusSizeBytes + " bytes)"); } - return mDelegate.doFinal(paddedInput, 0, paddedInput.length); + return mDelegate.doFinal(paddedInput, 0, paddedInput.length, additionalEntropy); } } } @@ -207,6 +213,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase @Override protected final int getAdditionalEntropyAmountForBegin() { + return 0; + } + + @Override + protected final int getAdditionalEntropyAmountForFinish() { return (isEncrypting()) ? getModulusSizeBytes() : 0; } } @@ -361,6 +372,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase @Override protected final int getAdditionalEntropyAmountForBegin() { + return 0; + } + + @Override + protected final int getAdditionalEntropyAmountForFinish() { return (isEncrypting()) ? mDigestOutputSizeBytes : 0; } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java index 898336d..954b71a 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java @@ -36,7 +36,7 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp } @Override - protected final int getAdditionalEntropyAmountForBegin() { + protected final int getAdditionalEntropyAmountForSign() { // No entropy required for this deterministic signature scheme. return 0; } @@ -92,8 +92,8 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp } @Override - protected final int getAdditionalEntropyAmountForBegin() { - return (isSigning()) ? SALT_LENGTH_BYTES : 0; + protected final int getAdditionalEntropyAmountForSign() { + return SALT_LENGTH_BYTES; } } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java index f072ae7..5cdcc41 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java @@ -198,15 +198,14 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi KeymasterArguments keymasterInputArgs = new KeymasterArguments(); addAlgorithmSpecificParametersToBegin(keymasterInputArgs); - byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( - appRandom, getAdditionalEntropyAmountForBegin()); OperationResult opResult = mKeyStore.begin( mKey.getAlias(), mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY, true, // permit aborting this operation if keystore runs out of resources keymasterInputArgs, - additionalEntropy); + null // no additional entropy for begin -- only finish might need some + ); if (opResult == null) { throw new KeyStoreConnectException(); } @@ -311,7 +310,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi byte[] signature; try { ensureKeystoreOperationInitialized(); - signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0); + + byte[] additionalEntropy = + KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( + appRandom, getAdditionalEntropyAmountForSign()); + signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0, additionalEntropy); } catch (InvalidKeyException | KeyStoreException e) { throw new SignatureException(e); } @@ -388,15 +391,14 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi /** * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's - * {@code begin} operation. + * {@code finish} operation when generating a signature. * - * <p>For signature verification, this should be {@code 0} because verification should not be - * consuming any entropy. For signature generation, this value should match (or exceed) the - * amount of Shannon entropy of the produced signature assuming the key and the message are - * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the - * RSA signature with PKCS#1 padding this should be {@code 0}. + * <p>This value should match (or exceed) the amount of Shannon entropy of the produced + * signature assuming the key and the message are known. For example, for ECDSA signature this + * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this + * should be {@code 0}. */ - protected abstract int getAdditionalEntropyAmountForBegin(); + protected abstract int getAdditionalEntropyAmountForSign(); /** * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index 831a106..3bd9d1d 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -247,14 +247,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { specBuilder = new KeyProtection.Builder( KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); + // Authorized to be used with any digest (including no digest). + specBuilder.setDigests(KeyProperties.DIGEST_NONE); } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { specBuilder = new KeyProtection.Builder( @@ -262,19 +256,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { | KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY); - specBuilder.setDigests( - KeyProperties.DIGEST_NONE, - KeyProperties.DIGEST_MD5, - KeyProperties.DIGEST_SHA1, - KeyProperties.DIGEST_SHA224, - KeyProperties.DIGEST_SHA256, - KeyProperties.DIGEST_SHA384, - KeyProperties.DIGEST_SHA512); + // Authorized to be used with any digest (including no digest). + specBuilder.setDigests(KeyProperties.DIGEST_NONE); specBuilder.setSignaturePaddings( KeyProperties.SIGNATURE_PADDING_RSA_PKCS1); + // Authorized to be used with any padding (including no padding). specBuilder.setEncryptionPaddings( - KeyProperties.ENCRYPTION_PADDING_NONE, - KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1); + KeyProperties.ENCRYPTION_PADDING_NONE); // Disable randomized encryption requirement to support encryption padding NONE // above. specBuilder.setRandomizedEncryptionRequired(false); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java index 47cd1d1..76804a9 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java @@ -210,6 +210,11 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp } @Override + protected final int getAdditionalEntropyAmountForFinish() { + return 0; + } + + @Override protected final void addAlgorithmSpecificParametersToBegin( @NonNull KeymasterArguments keymasterArgs) { if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) { diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 68c9c79..47aab74 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -611,9 +611,14 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { * * <p>This must be specified for keys which are used for signing/verification. For HMAC * keys, the set of digests defaults to the digest associated with the key algorithm (e.g., - * {@code SHA-256} for key algorithm {@code HmacSHA256} + * {@code SHA-256} for key algorithm {@code HmacSHA256}). * - * @see KeyProperties.Digest + * <p>For private keys used for TLS/SSL client or server authentication it is usually + * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is + * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use + * a private key to sign it. + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. */ @NonNull public Builder setDigests(@KeyProperties.DigestEnum String... digests) { @@ -629,6 +634,12 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { * * <p>This must be specified for keys which are used for encryption/decryption. * + * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it + * is usually necessary to authorize the use of no/any padding + * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}). This is because RSA decryption is + * required by some cipher suites, and some stacks request decryption using no padding + * whereas others request PKCS#1 padding. + * * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants. */ @NonNull diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 5af4181..403e814 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -368,6 +368,9 @@ public abstract class KeyProperties { /** * No encryption padding. + * + * <p><b>NOTE</b>: If a key is authorized to be used with no padding, then it can be used with + * any padding scheme. */ public static final String ENCRYPTION_PADDING_NONE = "NoPadding"; @@ -514,6 +517,9 @@ public abstract class KeyProperties { /** * No digest: sign/authenticate the raw message. + * + * <p><b>NOTE</b>: If a key is authorized to be used with no digest, then it can be used with + * any digest. */ public static final String DIGEST_NONE = "NONE"; diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 48c0ed0..432fc12 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -374,6 +374,12 @@ public final class KeyProtection implements ProtectionParameter { * * <p>This must be specified for keys which are used for encryption/decryption. * + * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it + * is usually necessary to authorize the use of no/any padding + * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}). This is because RSA decryption is + * required by some cipher suites, and some stacks request decryption using no padding + * whereas others request PKCS#1 padding. + * * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants. */ @NonNull @@ -408,6 +414,11 @@ public final class KeyProtection implements ProtectionParameter { * {@link Key#getAlgorithm()}. For asymmetric signing keys the set of digest algorithms * must be specified. * + * <p>For private keys used for TLS/SSL client or server authentication it is usually + * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is + * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use + * a private key to sign it. + * * <p>See {@link KeyProperties}.{@code DIGEST} constants. */ @NonNull diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java index 47b4996..9957e79 100644 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java @@ -35,8 +35,8 @@ import java.io.IOException; * amount of data in one go because the operations are marshalled via Binder. Secondly, the update * operation may consume less data than provided, in which case the caller has to buffer the * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and - * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement - * various JCA crypto primitives. + * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to conveniently + * implement various JCA crypto primitives. * * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional @@ -60,7 +60,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't * be reached. */ - OperationResult finish(); + OperationResult finish(byte[] additionalEntropy); } // Binder buffer is about 1MB, but it's shared between all active transactions of the process. @@ -192,7 +192,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS } @Override - public byte[] doFinal(byte[] input, int inputOffset, int inputLength) + public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy) throws KeyStoreException { if (inputLength == 0) { // No input provided -- simplify the rest of the code @@ -204,7 +204,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS byte[] output = update(input, inputOffset, inputLength); output = ArrayUtils.concat(output, flush()); - OperationResult opResult = mKeyStoreStream.finish(); + OperationResult opResult = mKeyStoreStream.finish(additionalEntropy); if (opResult == null) { throw new KeyStoreConnectException(); } else if (opResult.resultCode != KeyStore.NO_ERROR) { @@ -268,8 +268,8 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS } @Override - public OperationResult finish() { - return mKeyStore.finish(mOperationToken, null, null); + public OperationResult finish(byte[] additionalEntropy) { + return mKeyStore.finish(mOperationToken, null, null, additionalEntropy); } } } diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java index 2fb8f20..1c6de2d 100644 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java +++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java @@ -28,12 +28,13 @@ import android.security.KeyStoreException; * amount of data in one go because the operations are marshalled via Binder. Secondly, the update * operation may consume less data than provided, in which case the caller has to buffer the * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and - * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement - * various JCA crypto primitives. + * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to conveniently + * implement various JCA crypto primitives. * * @hide */ interface KeyStoreCryptoOperationStreamer { byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException; - byte[] doFinal(byte[] input, int inputOffset, int inputLength) throws KeyStoreException; + byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy) + throws KeyStoreException; } diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index 95b3eb3..260f380 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -172,7 +172,7 @@ public class GpsNetInitiatedHandler { * <p> * This is lazily created, so use {@link #setNINotification()}. */ - private Notification mNiNotification; + private Notification.Builder mNiNotificationBuilder; public GpsNetInitiatedHandler(Context context, INetInitiatedListener netInitiatedListener, @@ -367,29 +367,31 @@ public class GpsNetInitiatedHandler { ", message: " + message); // Construct Notification - if (mNiNotification == null) { - mNiNotification = new Notification(); - mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */ - mNiNotification.when = 0; + if (mNiNotificationBuilder == null) { + mNiNotificationBuilder = new Notification.Builder(mContext) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on) + .setWhen(0) + .setOngoing(true) + .setAutoCancel(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); } if (mPlaySounds) { - mNiNotification.defaults |= Notification.DEFAULT_SOUND; + mNiNotificationBuilder.setDefaults(Notification.DEFAULT_SOUND); } else { - mNiNotification.defaults &= ~Notification.DEFAULT_SOUND; + mNiNotificationBuilder.setDefaults(0); } - mNiNotification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_AUTO_CANCEL; - mNiNotification.tickerText = getNotifTicker(notif, mContext); - // if not to popup dialog immediately, pending intent will open the dialog Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent(); PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); - mNiNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mNiNotification.setLatestEventInfo(mContext, title, message, pi); + mNiNotificationBuilder.setTicker(getNotifTicker(notif, mContext)) + .setContentTitle(title) + .setContentText(message) + .setContentIntent(pi); - notificationManager.notifyAsUser(null, notif.notificationId, mNiNotification, + notificationManager.notifyAsUser(null, notif.notificationId, mNiNotificationBuilder.build(), UserHandle.ALL); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 742f570..26ece72 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -47,6 +47,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseLongArray; import android.util.TimeUtils; import java.io.ByteArrayOutputStream; @@ -84,6 +85,12 @@ class AlarmManagerService extends SystemService { // Minimum alarm recurrence interval private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis + // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle. + private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000; + + // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. + private static final long ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; + private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP; private static final int RTC_MASK = 1 << RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; @@ -123,8 +130,8 @@ class AlarmManagerService extends SystemService { int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; - ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<Alarm>(); - ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); + ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); + ArrayList<InFlight> mInFlight = new ArrayList<>(); final AlarmHandler mHandler = new AlarmHandler(); ClockReceiver mClockReceiver; InteractiveStateReceiver mInteractiveStateReceiver; @@ -141,8 +148,15 @@ class AlarmManagerService extends SystemService { long mNextNonWakeupDeliveryTime; long mLastTimeChangeClockTime; long mLastTimeChangeRealtime; + long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; int mNumTimeChanged; + /** + * For each uid, this is the last time we dispatched an "allow while idle" alarm, + * used to determine the earliest we can dispatch the next such alarm. + */ + final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray(); + private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = new SparseArray<>(); private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray = @@ -552,7 +566,7 @@ class AlarmManagerService extends SystemService { a.when = a.origWhen; long whenElapsed = convertToElapsed(a.when, a.type); final long maxElapsed; - if (a.whenElapsed == a.maxWhenElapsed) { + if (a.windowLength == AlarmManager.WINDOW_EXACT) { // Exact maxElapsed = whenElapsed; } else { @@ -580,6 +594,9 @@ class AlarmManagerService extends SystemService { } } + // Make sure we are using the correct ALLOW_WHILE_IDLE min time. + mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; + // Reschedule everything. rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); @@ -632,7 +649,7 @@ class AlarmManagerService extends SystemService { mTag = tag; } } - + static final class BroadcastStats { final int mUid; final String mPackageName; @@ -649,7 +666,7 @@ class AlarmManagerService extends SystemService { mPackageName = packageName; } } - + final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats = new SparseArray<ArrayMap<String, BroadcastStats>>(); @@ -751,7 +768,7 @@ class AlarmManagerService extends SystemService { void setImpl(int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, int flags, WorkSource workSource, - AlarmManager.AlarmClockInfo alarmClock) { + AlarmManager.AlarmClockInfo alarmClock, int callingUid) { if (operation == null) { Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; @@ -779,9 +796,8 @@ class AlarmManagerService extends SystemService { } if (triggerAtTime < 0) { - final long who = Binder.getCallingUid(); final long what = Binder.getCallingPid(); - Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who + Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + callingUid + " pid=" + what); triggerAtTime = 0; } @@ -797,12 +813,12 @@ class AlarmManagerService extends SystemService { maxElapsed = triggerElapsed; } else if (windowLength < 0) { maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); + // Fix this window in place, so that as time approaches we don't collapse it. + windowLength = maxElapsed - triggerElapsed; } else { maxElapsed = triggerElapsed + windowLength; } - final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { if (DEBUG_BATCH) { Slog.v(TAG, "set(" + operation + ") : type=" + type @@ -811,26 +827,20 @@ class AlarmManagerService extends SystemService { + " interval=" + interval + " flags=0x" + Integer.toHexString(flags)); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, - interval, operation, flags, true, workSource, alarmClock, userId); + interval, operation, flags, true, workSource, alarmClock, callingUid); } } private void setImplLocked(int type, long when, long whenElapsed, long windowLength, long maxWhen, long interval, PendingIntent operation, int flags, boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, - int userId) { + int uid) { Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, - operation, workSource, flags, alarmClock, userId); + operation, workSource, flags, alarmClock, uid); removeLocked(operation); setImplLocked(a, false, doValidate); } - private void updateNextWakeFromIdleFuzzLocked() { - if (mNextWakeFromIdle != null) { - - } - } - private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { // This is a special alarm that will put the system into idle until it goes off. @@ -862,7 +872,9 @@ class AlarmManagerService extends SystemService { } else if (mPendingIdleUntil != null) { // We currently have an idle until alarm scheduled; if the new alarm has // not explicitly stated it wants to run while idle, then put it on hold. - if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE|AlarmManager.FLAG_WAKE_FROM_IDLE)) + if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE + | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED + | AlarmManager.FLAG_WAKE_FROM_IDLE)) == 0) { mPendingWhileIdleAlarms.add(a); return; @@ -892,6 +904,7 @@ class AlarmManagerService extends SystemService { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a; + mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME; needRebatch = true; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { @@ -933,23 +946,45 @@ class AlarmManagerService extends SystemService { public void set(int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { + final int callingUid = Binder.getCallingUid(); if (workSource != null) { - getContext().enforceCallingPermission( + getContext().enforcePermission( android.Manifest.permission.UPDATE_DEVICE_STATS, - "AlarmManager.set"); + Binder.getCallingPid(), callingUid, "AlarmManager.set"); + } + + // No incoming callers can request either WAKE_FROM_IDLE or + // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. + flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE + | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); + + // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm + // manager when to come out of idle mode, which is only for DeviceIdleController. + if (callingUid != Process.SYSTEM_UID) { + flags &= ~AlarmManager.FLAG_IDLE_UNTIL; + } + + // If the caller is a core system component, and not calling to do work on behalf + // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. This means we + // will allow these alarms to go off as normal even while idle, with no timing + // restrictions. + if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) { + flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; } + // If this is an exact time alarm, then it can't be batched with other alarms. if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } + + // If this alarm is for an alarm clock, then it must be standalone and we will + // use it to wake early from idle if needed. if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; } - if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) { - flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE; - } + setImpl(type, triggerAtTime, windowLength, interval, operation, - flags, workSource, alarmClock); + flags, workSource, alarmClock, callingUid); } @Override @@ -1126,6 +1161,22 @@ class AlarmManagerService extends SystemService { pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); pw.println(); + pw.print("mAllowWhileIdleMinTime="); + TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw); + pw.println(); + if (mLastAllowWhileIdleDispatch.size() > 0) { + pw.println("Last allow while idle dispatch times:"); + for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) { + pw.print(" UID "); + UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i)); + pw.print(": "); + TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i), + nowELAPSED, pw); + pw.println(); + } + } + pw.println(); + if (mLog.dump(pw, " Recent problems", " ")) { pw.println(); } @@ -1322,7 +1373,7 @@ class AlarmManagerService extends SystemService { for (int j = 0; j < M; j++) { Alarm a = alarms.get(j); if (a.alarmClock != null) { - final int userId = a.userId; + final int userId = UserHandle.getUserId(a.uid); if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Found AlarmClockInfo at " + @@ -1531,6 +1582,11 @@ class AlarmManagerService extends SystemService { mPendingWhileIdleAlarms.remove(i); } } + for (int i = mLastAllowWhileIdleDispatch.size() - 1; i >= 0; i--) { + if (UserHandle.getUserId(mLastAllowWhileIdleDispatch.keyAt(i)) == userHandle) { + mLastAllowWhileIdleDispatch.removeAt(i); + } + } if (didRemove) { if (DEBUG_BATCH) { @@ -1666,6 +1722,25 @@ class AlarmManagerService extends SystemService { final int N = batch.size(); for (int i = 0; i < N; i++) { Alarm alarm = batch.get(i); + + if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can + // schedule such alarms. + long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0); + long minTime = lastTime + mAllowWhileIdleMinTime; + if (nowELAPSED < minTime) { + // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE + // alarm went off for this app. Reschedule the alarm to be in the + // correct time period. + alarm.whenElapsed = minTime; + if (alarm.maxWhenElapsed < minTime) { + alarm.maxWhenElapsed = minTime; + } + setImplLocked(alarm, true, false); + continue; + } + } + alarm.count = 1; triggerList.add(alarm); if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { @@ -1695,7 +1770,7 @@ class AlarmManagerService extends SystemService { setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), alarm.repeatInterval, alarm.operation, alarm.flags, true, - alarm.workSource, alarm.alarmClock, alarm.userId); + alarm.workSource, alarm.alarmClock, alarm.uid); } if (alarm.wakeup) { @@ -1749,19 +1824,19 @@ class AlarmManagerService extends SystemService { public final String tag; public final WorkSource workSource; public final int flags; + public final AlarmManager.AlarmClockInfo alarmClock; + public final int uid; public int count; public long when; public long windowLength; public long whenElapsed; // 'when' in the elapsed time base public long maxWhenElapsed; // also in the elapsed time base public long repeatInterval; - public final AlarmManager.AlarmClockInfo alarmClock; - public final int userId; public PriorityClass priorityClass; public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, long _interval, PendingIntent _op, WorkSource _ws, int _flags, - AlarmManager.AlarmClockInfo _info, int _userId) { + AlarmManager.AlarmClockInfo _info, int _uid) { type = _type; origWhen = _when; wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP @@ -1776,7 +1851,7 @@ class AlarmManagerService extends SystemService { workSource = _ws; flags = _flags; alarmClock = _info; - userId = _userId; + uid = _uid; } public static String makeTag(PendingIntent pi, int type) { @@ -1812,7 +1887,7 @@ class AlarmManagerService extends SystemService { pw.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw); } pw.println(); - pw.print(prefix); pw.print("window="); pw.print(windowLength); + pw.print(prefix); pw.print("window="); TimeUtils.formatDuration(windowLength, pw); pw.print(" repeatInterval="); pw.print(repeatInterval); pw.print(" count="); pw.print(count); pw.print(" flags=0x"); pw.println(Integer.toHexString(flags)); @@ -1925,6 +2000,11 @@ class AlarmManagerService extends SystemService { mInFlight.add(inflight); mBroadcastRefCount++; + if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. + mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED); + } + final BroadcastStats bs = inflight.mBroadcastStats; bs.count++; if (bs.nesting == 0) { @@ -2196,7 +2276,8 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for time tick events. setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, - 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null); + 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null, + Process.myUid()); } public void scheduleDateChangedEvent() { @@ -2210,7 +2291,7 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for date change events. setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, - AlarmManager.FLAG_STANDALONE, workSource, null); + AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid()); } } @@ -2243,6 +2324,7 @@ class AlarmManagerService extends SystemService { IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_USER_STOPPED); + sdFilter.addAction(Intent.ACTION_UID_REMOVED); getContext().registerReceiver(this, sdFilter); } @@ -2267,6 +2349,11 @@ class AlarmManagerService extends SystemService { if (userHandle >= 0) { removeUserLocked(userHandle); } + } else if (Intent.ACTION_UID_REMOVED.equals(action)) { + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid >= 0) { + mLastAllowWhileIdleDispatch.delete(uid); + } } else { if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 9c6e16f..f645764 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3289,7 +3289,6 @@ public class ConnectivityService extends IConnectivityManager.Stub CharSequence title; CharSequence details; int icon; - Notification notification = new Notification(); if (notifyType == NotificationType.NO_INTERNET && networkType == ConnectivityManager.TYPE_WIFI) { title = r.getString(R.string.wifi_no_internet, 0); @@ -3324,14 +3323,17 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } - notification.when = 0; - notification.icon = icon; - notification.flags = Notification.FLAG_AUTO_CANCEL; - notification.tickerText = title; - notification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); - notification.contentIntent = intent; + Notification notification = new Notification.Builder(mContext) + .setWhen(0) + .setSmallIcon(icon) + .setAutoCancel(true) + .setTicker(title) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(details) + .setContentIntent(intent) + .build(); try { notificationManager.notify(NOTIFICATION_ID, id, notification); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 6e6fb7f..a5d536e 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -71,6 +71,7 @@ import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -212,7 +213,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private NotificationManager mNotificationManager; private KeyguardManager mKeyguardManager; private StatusBarManagerService mStatusBar; - private Notification mImeSwitcherNotification; + private Notification.Builder mImeSwitcherNotification; private PendingIntent mImeSwitchPendingIntent; private boolean mShowOngoingImeSwitcherForPhones; private boolean mNotificationShown; @@ -798,18 +799,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); - mImeSwitcherNotification = new Notification(); - mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default; - mImeSwitcherNotification.when = 0; - mImeSwitcherNotification.flags = Notification.FLAG_ONGOING_EVENT; - mImeSwitcherNotification.tickerText = null; - mImeSwitcherNotification.defaults = 0; // please be quiet - mImeSwitcherNotification.sound = null; - mImeSwitcherNotification.vibrate = null; - - // Tag this notification specially so SystemUI knows it's important - mImeSwitcherNotification.extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true); - mImeSwitcherNotification.category = Notification.CATEGORY_SYSTEM; + Bundle extras = new Bundle(); + extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true); + mImeSwitcherNotification = new Notification.Builder(mContext) + .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default) + .setWhen(0) + .setOngoing(true) + .addExtras(extras) + .setCategory(Notification.CATEGORY_SYSTEM) + .setColor(com.android.internal.R.color.system_notification_accent_color); Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); @@ -1766,11 +1764,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.string.select_input_method); final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName( mContext, imi, mCurrentSubtype); - - mImeSwitcherNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mImeSwitcherNotification.setLatestEventInfo( - mContext, title, summary, mImeSwitchPendingIntent); + mImeSwitcherNotification.setContentTitle(title) + .setContentText(summary) + .setContentIntent(mImeSwitchPendingIntent); if ((mNotificationManager != null) && !mWindowManagerService.hasNavigationBar()) { if (DEBUG) { @@ -1778,7 +1774,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mNotificationManager.notifyAsUser(null, com.android.internal.R.string.select_input_method, - mImeSwitcherNotification, UserHandle.ALL); + mImeSwitcherNotification.build(), UserHandle.ALL); mNotificationShown = true; } } else { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 64f3070..0b67ad8 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -601,21 +601,22 @@ final class UiModeManagerService extends SystemService { if (mCarModeEnabled) { Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); - Notification n = new Notification(); - n.icon = R.drawable.stat_notify_car_mode; - n.defaults = Notification.DEFAULT_LIGHTS; - n.flags = Notification.FLAG_ONGOING_EVENT; - n.when = 0; - n.color = context.getColor( - com.android.internal.R.color.system_notification_accent_color); - n.setLatestEventInfo( - context, - context.getString(R.string.car_mode_disable_notification_title), - context.getString(R.string.car_mode_disable_notification_message), - PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0, - null, UserHandle.CURRENT)); + Notification.Builder n = new Notification.Builder(context) + .setSmallIcon(R.drawable.stat_notify_car_mode) + .setDefaults(Notification.DEFAULT_LIGHTS) + .setOngoing(true) + .setWhen(0) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle( + context.getString(R.string.car_mode_disable_notification_title)) + .setContentText( + context.getString(R.string.car_mode_disable_notification_message)) + .setContentIntent( + PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0, + null, UserHandle.CURRENT)); mNotificationManager.notifyAsUser(null, - R.string.car_mode_disable_notification_title, n, UserHandle.ALL); + R.string.car_mode_disable_notification_title, n.build(), UserHandle.ALL); } else { mNotificationManager.cancelAsUser(null, R.string.car_mode_disable_notification_title, UserHandle.ALL); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 49d9988..3456dbc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2005,8 +2005,6 @@ public class AccountManagerService String authTokenLabel = intent.getStringExtra( GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL); - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); final String titleAndSubtitle = mContext.getString(R.string.permission_request_notification_with_subtitle, account.name); @@ -2019,11 +2017,16 @@ public class AccountManagerService } UserHandle user = new UserHandle(userId); Context contextForUser = getContextForUser(user); - n.color = contextForUser.getColor( - com.android.internal.R.color.system_notification_accent_color); - n.setLatestEventInfo(contextForUser, title, subtitle, - PendingIntent.getActivityAsUser(mContext, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, user)); + Notification n = new Notification.Builder(contextForUser) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setWhen(0) + .setColor(contextForUser.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(subtitle) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, user)) + .build(); installNotification(getCredentialPermissionNotificationId( account, authTokenType, uid), n, user); } @@ -3542,19 +3545,21 @@ public class AccountManagerService } else { final Integer notificationId = getSigninRequiredNotificationId(accounts, account); intent.addCategory(String.valueOf(notificationId)); - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); UserHandle user = new UserHandle(userId); Context contextForUser = getContextForUser(user); final String notificationTitleFormat = contextForUser.getText(R.string.notification_title).toString(); - n.color = contextForUser.getColor( - com.android.internal.R.color.system_notification_accent_color); - n.setLatestEventInfo(contextForUser, - String.format(notificationTitleFormat, account.name), - message, PendingIntent.getActivityAsUser( - mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, - null, user)); + Notification n = new Notification.Builder(contextForUser) + .setWhen(0) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setColor(contextForUser.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(String.format(notificationTitleFormat, account.name)) + .setContentText(message) + .setContentIntent(PendingIntent.getActivityAsUser( + mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, + null, user)) + .build(); installNotification(notificationId, n, user); } } finally { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 029a3b2..421ba86 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1714,22 +1714,20 @@ public final class ActivityManagerService extends ActivityManagerNative Context context = mContext.createPackageContext(process.info.packageName, 0); String text = mContext.getString(R.string.heavy_weight_notification, context.getApplicationInfo().loadLabel(context.getPackageManager())); - Notification notification = new Notification(); - notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon; - notification.when = 0; - notification.flags = Notification.FLAG_ONGOING_EVENT; - notification.tickerText = text; - notification.defaults = 0; // please be quiet - notification.sound = null; - notification.vibrate = null; - notification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - notification.setLatestEventInfo(context, text, - mContext.getText(R.string.heavy_weight_notification_detail), - PendingIntent.getActivityAsUser(mContext, 0, root.intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, - new UserHandle(root.userId))); - + Notification notification = new Notification.Builder(context) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setWhen(0) + .setOngoing(true) + .setTicker(text) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(text) + .setContentText( + mContext.getText(R.string.heavy_weight_notification_detail)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + root.intent, PendingIntent.FLAG_CANCEL_CURRENT, null, + new UserHandle(root.userId))) + .build(); try { int[] outId = new int[1]; inm.enqueueNotificationWithTag("android", "android", null, @@ -1948,20 +1946,10 @@ public final class ActivityManagerService extends ActivityManagerNative } String text = mContext.getString(R.string.dump_heap_notification, procName); - Notification notification = new Notification(); - notification.icon = com.android.internal.R.drawable.stat_sys_adb; - notification.when = 0; - notification.flags = Notification.FLAG_ONGOING_EVENT|Notification.FLAG_AUTO_CANCEL; - notification.tickerText = text; - notification.defaults = 0; // please be quiet - notification.sound = null; - notification.vibrate = null; - notification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); + + Intent deleteIntent = new Intent(); deleteIntent.setAction(DumpHeapActivity.ACTION_DELETE_DUMPHEAP); - notification.deleteIntent = PendingIntent.getBroadcastAsUser(mContext, 0, - deleteIntent, 0, UserHandle.OWNER); Intent intent = new Intent(); intent.setClassName("android", DumpHeapActivity.class.getName()); intent.putExtra(DumpHeapActivity.KEY_PROCESS, procName); @@ -1970,11 +1958,23 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage); } int userId = UserHandle.getUserId(uid); - notification.setLatestEventInfo(mContext, text, - mContext.getText(R.string.dump_heap_notification_detail), - PendingIntent.getActivityAsUser(mContext, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, - new UserHandle(userId))); + Notification notification = new Notification.Builder(mContext) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setWhen(0) + .setOngoing(true) + .setAutoCancel(true) + .setTicker(text) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(text) + .setContentText( + mContext.getText(R.string.dump_heap_notification_detail)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + intent, PendingIntent.FLAG_CANCEL_CURRENT, null, + new UserHandle(userId))) + .setDeleteIntent(PendingIntent.getBroadcastAsUser(mContext, 0, + deleteIntent, 0, UserHandle.OWNER)) + .build(); try { int[] outId = new int[1]; diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 897300f..c1aaf07 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -130,7 +130,8 @@ public class Tethering extends BaseNetworkObserver { private StateMachine mTetherMasterSM; - private Notification mTetheredNotification; + private Notification.Builder mTetheredNotificationBuilder; + private int mLastNotificationId; private boolean mRndisEnabled; // track the RNDIS function enabled state private boolean mUsbTetherRequested; // true if USB tethering should be started @@ -450,12 +451,13 @@ public class Tethering extends BaseNetworkObserver { return; } - if (mTetheredNotification != null) { - if (mTetheredNotification.icon == icon) { + if (mLastNotificationId != 0) { + if (mLastNotificationId == icon) { return; } - notificationManager.cancelAsUser(null, mTetheredNotification.icon, + notificationManager.cancelAsUser(null, mLastNotificationId, UserHandle.ALL); + mLastNotificationId = 0; } Intent intent = new Intent(); @@ -470,31 +472,32 @@ public class Tethering extends BaseNetworkObserver { CharSequence message = r.getText(com.android.internal.R.string. tethered_notification_message); - if (mTetheredNotification == null) { - mTetheredNotification = new Notification(); - mTetheredNotification.when = 0; - } - mTetheredNotification.icon = icon; - mTetheredNotification.defaults &= ~Notification.DEFAULT_SOUND; - mTetheredNotification.flags = Notification.FLAG_ONGOING_EVENT; - mTetheredNotification.tickerText = title; - mTetheredNotification.visibility = Notification.VISIBILITY_PUBLIC; - mTetheredNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mTetheredNotification.setLatestEventInfo(mContext, title, message, pi); - mTetheredNotification.category = Notification.CATEGORY_STATUS; - - notificationManager.notifyAsUser(null, mTetheredNotification.icon, - mTetheredNotification, UserHandle.ALL); + if (mTetheredNotificationBuilder == null) { + mTetheredNotificationBuilder = new Notification.Builder(mContext); + mTetheredNotificationBuilder.setWhen(0) + .setOngoing(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setCategory(Notification.CATEGORY_STATUS); + } + mTetheredNotificationBuilder.setSmallIcon(icon) + .setContentTitle(title) + .setContentText(message) + .setContentIntent(pi); + mLastNotificationId = icon; + + notificationManager.notifyAsUser(null, mLastNotificationId, + mTetheredNotificationBuilder.build(), UserHandle.ALL); } private void clearTetheredNotification() { NotificationManager notificationManager = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null && mTetheredNotification != null) { - notificationManager.cancelAsUser(null, mTetheredNotification.icon, + if (notificationManager != null && mLastNotificationId != 0) { + notificationManager.cancelAsUser(null, mLastNotificationId, UserHandle.ALL); - mTetheredNotification = null; + mLastNotificationId = 0; } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 3dc282b..f222dba 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -3260,16 +3260,18 @@ public class SyncManager { R.string.contentServiceTooManyDeletesNotificationDesc); Context contextForUser = getContextForUser(user); - Notification notification = - new Notification(R.drawable.stat_notify_sync_error, - mContext.getString(R.string.contentServiceSync), - System.currentTimeMillis()); - notification.color = contextForUser.getColor( - com.android.internal.R.color.system_notification_accent_color); - notification.setLatestEventInfo(contextForUser, - contextForUser.getString(R.string.contentServiceSyncNotificationTitle), - String.format(tooManyDeletesDescFormat.toString(), authorityName), - pendingIntent); + Notification notification = new Notification.Builder(contextForUser) + .setSmallIcon(R.drawable.stat_notify_sync_error) + .setTicker(mContext.getString(R.string.contentServiceSync)) + .setWhen(System.currentTimeMillis()) + .setColor(contextForUser.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(contextForUser.getString( + R.string.contentServiceSyncNotificationTitle)) + .setContentText( + String.format(tooManyDeletesDescFormat.toString(), authorityName)) + .setContentIntent(pendingIntent) + .build(); notification.flags |= Notification.FLAG_ONGOING_EVENT; mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), notification, user); diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 7e66cd1..7b1ac5c 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import android.app.AlarmManager; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; @@ -34,6 +35,8 @@ import java.util.concurrent.atomic.AtomicBoolean; public class BackgroundDexOptService extends JobService { static final String TAG = "BackgroundDexOptService"; + static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR; + static final int BACKGROUND_DEXOPT_JOB = 800; private static ComponentName sDexoptServiceName = new ComponentName( "android", @@ -46,11 +49,12 @@ public class BackgroundDexOptService extends JobService { final AtomicBoolean mIdleTime = new AtomicBoolean(false); - public static void schedule(Context context) { + public static void schedule(Context context, long minLatency) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName) .setRequiresDeviceIdle(true) .setRequiresCharging(true) + .setMinimumLatency(minLatency) .build(); js.schedule(job); } @@ -62,6 +66,7 @@ public class BackgroundDexOptService extends JobService { (PackageManagerService)ServiceManager.getService("package"); if (pm.isStorageLow()) { + schedule(BackgroundDexOptService.this, RETRY_LATENCY); return false; } final ArraySet<String> pkgs = pm.getPackagesThatNeedDexOpt(); @@ -77,7 +82,7 @@ public class BackgroundDexOptService extends JobService { for (String pkg : pkgs) { if (!mIdleTime.get()) { // stopped while still working, so we need to reschedule - schedule(BackgroundDexOptService.this); + schedule(BackgroundDexOptService.this, 0); return; } if (sFailedPackageNames.contains(pkg)) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 925a609..29c65db 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -958,7 +958,7 @@ public final class SystemServer { try { Slog.i(TAG, "BackgroundDexOptService"); - BackgroundDexOptService.schedule(context); + BackgroundDexOptService.schedule(context, 0); } catch (Throwable e) { reportWtf("starting BackgroundDexOptService", e); } diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index c105491..dae7cc5 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -76,5 +76,6 @@ android:authorities="com.google.android.test.activity.single_user" android:singleUser="true" android:exported="true" /> <receiver android:name="TrackTimeReceiver" /> + <receiver android:name="AlarmSpamReceiver" /> </application> </manifest> 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 ddcfd9e..94cbabf 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -22,6 +22,7 @@ import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; import android.content.ActivityNotFoundException; @@ -36,6 +37,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.graphics.Bitmap; @@ -58,6 +60,7 @@ public class ActivityTestMain extends Activity { static final String KEY_CONFIGURATION = "configuration"; ActivityManager mAm; + AlarmManager mAlarm; Configuration mOverrideConfig; int mSecondUser; @@ -66,6 +69,7 @@ public class ActivityTestMain extends Activity { ServiceConnection mIsolatedConnection; static final int MSG_SPAM = 1; + static final int MSG_SPAM_ALARM = 2; final Handler mHandler = new Handler() { @Override @@ -82,6 +86,15 @@ public class ActivityTestMain extends Activity { startActivity(intent, options); scheduleSpam(!fg); } break; + case MSG_SPAM_ALARM: { + long when = SystemClock.elapsedRealtime(); + Intent intent = new Intent(ActivityTestMain.this, AlarmSpamReceiver.class); + intent.setAction("com.example.SPAM_ALARM=" + when); + PendingIntent pi = PendingIntent.getBroadcast(ActivityTestMain.this, + 0, intent, 0); + mAlarm.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, when+(30*1000), pi); + scheduleSpamAlarm(30*1000); + } break; } super.handleMessage(msg); } @@ -146,6 +159,7 @@ public class ActivityTestMain extends Activity { Log.i(TAG, "Referrer: " + getReferrer()); mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE); + mAlarm = (AlarmManager)getSystemService(ALARM_SERVICE); if (savedInstanceState != null) { mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION); if (mOverrideConfig != null) { @@ -436,6 +450,13 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Spam idle alarm").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSpamAlarm(0); + return true; + } + }); return true; } @@ -467,6 +488,7 @@ public class ActivityTestMain extends Activity { @Override protected void onStop() { super.onStop(); + mHandler.removeMessages(MSG_SPAM_ALARM); for (ServiceConnection conn : mConnections) { unbindService(conn); } @@ -536,6 +558,12 @@ public class ActivityTestMain extends Activity { mHandler.sendMessageDelayed(msg, 500); } + void scheduleSpamAlarm(long delay) { + mHandler.removeMessages(MSG_SPAM_ALARM); + Message msg = mHandler.obtainMessage(MSG_SPAM_ALARM); + mHandler.sendMessageDelayed(msg, delay); + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, diff --git a/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java new file mode 100644 index 0000000..0cb1ffb --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java @@ -0,0 +1,31 @@ +/* + * 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.google.android.test.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Log; + +public class AlarmSpamReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.i("AlarmSpamReceiver", "Received spam = " + intent); + } +} diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java index 947ea78..2e51570 100644 --- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java +++ b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java @@ -109,14 +109,20 @@ public class FixVibrateSetting extends Activity implements View.OnClickListener } private void test() { - Notification n = new Notification(R.drawable.stat_sys_warning, "Test notification", - System.currentTimeMillis()); Intent intent = new Intent(this, FixVibrateSetting.class); PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0); - n.setLatestEventInfo(this, "Test notification", "Test notification", pending); - n.vibrate = new long[] { 0, 700, 500, 1000 }; - n.flags |= Notification.FLAG_AUTO_CANCEL; + Notification n = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sys_warning) + .setTicker("Test notification") + .setWhen(System.currentTimeMillis()) + .setContentTitle("Test notification") + .setContentText("Test notification") + .setContentIntent(pending) + .setVibrate(new long[] { 0, 700, 500, 1000 }) + .setAutoCancel(true) + .build(); + mNotificationManager.notify(1, n); } } diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java index 7691e64..fc3f390 100644 --- a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java +++ b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java @@ -26,15 +26,18 @@ public class SchedulerService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { - Notification status = new Notification(R.drawable.stat_happy, null, - System.currentTimeMillis()); - status.flags |= Notification.FLAG_ONGOING_EVENT; - status.setLatestEventInfo(this, "Scheduler Test running", - "Scheduler Test running", PendingIntent.getActivity(this, 0, - new Intent(this, FrameworkPerfActivity.class) - .setAction(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_LAUNCHER) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0)); + Notification status = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_happy) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Scheduler Test running") + .setContentText("Scheduler Test running") + .setContentIntent(PendingIntent.getActivity(this, 0, + new Intent(this, FrameworkPerfActivity.class) + .setAction(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0)) + .setOngoing(true) + .build(); startForeground(1, status); return START_STICKY; } diff --git a/tests/StatusBar/res/drawable-hdpi/stat_sys_warning.png b/tests/StatusBar/res/drawable-hdpi/stat_sys_warning.png Binary files differnew file mode 100644 index 0000000..dbaf944 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/stat_sys_warning.png diff --git a/tests/StatusBar/res/drawable-mdpi/stat_sys_warning.png b/tests/StatusBar/res/drawable-mdpi/stat_sys_warning.png Binary files differnew file mode 100644 index 0000000..168f8f6 --- /dev/null +++ b/tests/StatusBar/res/drawable-mdpi/stat_sys_warning.png diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java index ba160b1..67b9d77 100644 --- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java +++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java @@ -25,7 +25,6 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.os.Environment; import android.os.Vibrator; import android.os.Handler; import android.os.UserHandle; @@ -85,7 +84,7 @@ public class NotificationTestList extends TestActivity } private Test[] mTests = new Test[] { - new Test("Off and sound") { + new Test("Off") { public void run() { PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = @@ -94,9 +93,12 @@ public class NotificationTestList extends TestActivity pm.goToSleep(SystemClock.uptimeMillis()); - Notification n = new Notification(); - n.sound = Uri.parse("file://" + Environment.getExternalStorageDirectory() + - "/virtual-void.mp3"); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setContentTitle(name) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .build(); Log.d(TAG, "n.sound=" + n.sound); mNM.notify(1, n); @@ -114,122 +116,120 @@ public class NotificationTestList extends TestActivity } }, - new Test("Button") { + new Test("Custom Button") { public void run() { - Notification n = new Notification(R.drawable.icon1, null, - mActivityCreateTime); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setOngoing(true) + .build(); n.contentView = new RemoteViews(getPackageName(), R.layout.button_notification); - n.flags |= Notification.FLAG_ONGOING_EVENT; - n.contentIntent = makeIntent(); n.contentView.setOnClickPendingIntent(R.id.button, makeIntent2()); mNM.notify(1, n); } }, - new Test("custom intent on text view") { + new Test("Action Button") { public void run() { - Notification n = new Notification(R.drawable.icon1, null, - mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is a notification!!!", null); - n.contentView.setOnClickPendingIntent(com.android.internal.R.id.text, - makeIntent2()); - mNM.notify(1, n); - } - }, + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setOngoing(true) + .addAction(R.drawable.ic_statusbar_chat, "Button", makeIntent2()) + .build(); - new Test("Ticker 1 line") { - public void run() { - Notification n = new Notification(R.drawable.icon1, "tick tick tick", - mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is a notification!!!", makeIntent()); mNM.notify(1, n); } }, - new Test("No view") { + new Test("with intent") { public void run() { - Notification n = new Notification(R.drawable.icon1, "No view", - System.currentTimeMillis()); - mNM.notify(1, n); - } - }, + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle("Persistent #1") + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent2()) + .setOngoing(true) + .build(); - new Test("No intent") { - public void run() { - Notification n = new Notification(R.drawable.icon1, "No intent", - System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "No intent", - "No intent", null); mNM.notify(1, n); } }, - new Test("Layout") { + new Test("Whens") { public void run() { - Notification n; + Notification.Builder n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setContentTitle(name) + .setOngoing(true); - n = new Notification(NotificationTestList.this, - R.drawable.ic_statusbar_missedcall, - null, System.currentTimeMillis()-(1000*60*60*24), - "(453) 123-2328", - "", null); - n.flags |= Notification.FLAG_ONGOING_EVENT; + mNM.notify(1, n.setContentTitle("(453) 123-2328") + .setWhen(System.currentTimeMillis()-(1000*60*60*24)) + .build()); - mNM.notify(1, n); - - mNM.notify(2, new Notification(NotificationTestList.this, - R.drawable.ic_statusbar_email, - null, System.currentTimeMillis(), - "Mark Willem, Me (2)", - "Re: Didn't you get the memo?", null)); + mNM.notify(1, n.setContentTitle("Mark Willem, Me (2)") + .setWhen(System.currentTimeMillis()) + .build()); - mNM.notify(3, new Notification(NotificationTestList.this, - R.drawable.ic_statusbar_chat, - null, System.currentTimeMillis()+(1000*60*60*24), - "Sophia Winterlanden", - "Lorem ipsum dolor sit amet.", null)); + mNM.notify(1, n.setContentTitle("Sophia Winterlanden") + .setWhen(System.currentTimeMillis() + (1000 * 60 * 60 * 24)) + .build()); } }, new Test("Bad Icon #1 (when=create)") { public void run() { - Notification n = new Notification(R.layout.chrono_notification /* not an icon */, - null, mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.layout.chrono_notification /* not an icon */) + .setWhen(mActivityCreateTime) + .setContentTitle("Persistent #1") + .setContentText("This is the same notification!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(1, n); } }, new Test("Bad Icon #1 (when=now)") { public void run() { - Notification n = new Notification(R.layout.chrono_notification /* not an icon */, - null, System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.layout.chrono_notification /* not an icon */) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Persistent #1") + .setContentText("This is the same notification!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(1, n); } }, new Test("Null Icon #1 (when=now)") { public void run() { - Notification n = new Notification(0, null, System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(0) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Persistent #1") + .setContentText("This is the same notification!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(1, n); } }, new Test("Bad resource #1 (when=create)") { public void run() { - Notification n = new Notification(R.drawable.icon2, - null, mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setWhen(mActivityCreateTime) + .setContentTitle("Persistent #1") + .setContentText("This is the same notification!!") + .setContentIntent(makeIntent()) + .build(); n.contentView.setInt(1 /*bogus*/, "bogus method", 666); mNM.notify(1, n); } @@ -237,29 +237,18 @@ public class NotificationTestList extends TestActivity new Test("Bad resource #1 (when=now)") { public void run() { - Notification n = new Notification(R.drawable.icon2, - null, System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Persistent #1") + .setContentText("This is the same notification!!") + .setContentIntent(makeIntent()) + .build(); n.contentView.setInt(1 /*bogus*/, "bogus method", 666); mNM.notify(1, n); } }, - - new Test("Bad resource #3") { - public void run() - { - Notification n = new Notification(NotificationTestList.this, - R.drawable.ic_statusbar_missedcall, - null, System.currentTimeMillis()-(1000*60*60*24), - "(453) 123-2328", - "", null); - n.contentView.setInt(1 /*bogus*/, "bogus method", 666); - mNM.notify(3, n); - } - }, - new Test("Times") { public void run() { @@ -278,22 +267,25 @@ public class NotificationTestList extends TestActivity new Runnable() { public void run() { Log.d(TAG, "Stress - Ongoing/Latest 0"); - Notification n = new Notification(NotificationTestList.this, - R.drawable.icon3, - null, System.currentTimeMillis(), "Stress - Ongoing", - "Notify me!!!", null); - n.flags |= Notification.FLAG_ONGOING_EVENT; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon3) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Stress - Ongoing") + .setContentText("Notify me!!!") + .setOngoing(true) + .build(); mNM.notify(1, n); } }, new Runnable() { public void run() { Log.d(TAG, "Stress - Ongoing/Latest 1"); - Notification n = new Notification(NotificationTestList.this, - R.drawable.icon4, - null, System.currentTimeMillis(), "Stress - Latest", - "Notify me!!!", null); - //n.flags |= Notification.FLAG_ONGOING_EVENT; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon4) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Stress - Latest") + .setContentText("Notify me!!!") + .build(); mNM.notify(1, n); } } @@ -302,12 +294,15 @@ public class NotificationTestList extends TestActivity new Test("Long") { public void run() { - Notification n = new Notification(); - n.defaults |= Notification.DEFAULT_SOUND ; - n.vibrate = new long[] { - 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, - 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, - 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 }; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setContentTitle(name) + .setDefaults(Notification.DEFAULT_SOUND) + .setVibrate(new long[] { + 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, + 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, + 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 }) + .build(); mNM.notify(1, n); } }, @@ -320,21 +315,19 @@ public class NotificationTestList extends TestActivity Thread t = new Thread() { public void run() { int x = 0; + final Notification.Builder n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setContentTitle(name) + .setOngoing(true); + while (!mProgressDone) { - Notification n = new Notification(R.drawable.icon1, null, - PROGRESS_UPDATES_WHEN + n.setWhen(PROGRESS_UPDATES_WHEN ? System.currentTimeMillis() : mActivityCreateTime); - RemoteViews v = new RemoteViews(getPackageName(), - R.layout.progress_notification); - - v.setProgressBar(R.id.progress_bar, 100, x, false); - v.setTextViewText(R.id.status_text, "Progress: " + x + "%"); - - n.contentView = v; - n.flags |= Notification.FLAG_ONGOING_EVENT; - - mNM.notify(500, n); + n.setProgress(100, x, false); + n.setContentText("Progress: " + x + "%"); + + mNM.notify(500, n.build()); x = (x + 7) % 100; try { @@ -359,11 +352,12 @@ public class NotificationTestList extends TestActivity new Test("Blue Lights") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0xff0000ff; - n.ledOnMS = 1; - n.ledOffMS = 0; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -371,11 +365,12 @@ public class NotificationTestList extends TestActivity new Test("Red Lights") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0xffff0000; - n.ledOnMS = 1; - n.ledOffMS = 0; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0xffff0000, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -383,11 +378,12 @@ public class NotificationTestList extends TestActivity new Test("Yellow Lights") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0xffffff00; - n.ledOnMS = 1; - n.ledOffMS = 0; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0xffffff00, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -395,11 +391,12 @@ public class NotificationTestList extends TestActivity new Test("Lights off") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0x00000000; - n.ledOnMS = 0; - n.ledOffMS = 0; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0x00000000, 0, 0) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -407,11 +404,12 @@ public class NotificationTestList extends TestActivity new Test("Blue Blinking Slow") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0xff0000ff; - n.ledOnMS = 1300; - n.ledOffMS = 1300; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0xff0000ff, 1300, 1300) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -419,11 +417,12 @@ public class NotificationTestList extends TestActivity new Test("Blue Blinking Fast") { public void run() { - Notification n = new Notification(); - n.flags |= Notification.FLAG_SHOW_LIGHTS; - n.ledARGB = 0xff0000ff; - n.ledOnMS = 300; - n.ledOffMS = 300; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setLights(0xff0000ff, 300, 300) + .setDefaults(Notification.DEFAULT_LIGHTS) + .build(); mNM.notify(1, n); } }, @@ -431,8 +430,11 @@ public class NotificationTestList extends TestActivity new Test("Default All") { public void run() { - Notification n = new Notification(); - n.defaults |= Notification.DEFAULT_ALL; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setDefaults(Notification.DEFAULT_ALL) + .build(); mNM.notify(1, n); } }, @@ -440,20 +442,12 @@ public class NotificationTestList extends TestActivity new Test("Default All, once") { public void run() { - Notification n = new Notification(); - n.defaults |= Notification.DEFAULT_ALL; - n.flags |= Notification.FLAG_ONLY_ALERT_ONCE ; - mNM.notify(1, n); - } - }, - - new Test("Content Sound") { - public void run() - { - Notification n = new Notification(); - n.sound = Uri.parse( - "content://media/internal/audio/media/7"); - + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle(name) + .setOnlyAlertOnce(true) + .setDefaults(Notification.DEFAULT_ALL) + .build(); mNM.notify(1, n); } }, @@ -461,10 +455,12 @@ public class NotificationTestList extends TestActivity new Test("Resource Sound") { public void run() { - Notification n = new Notification(); - n.sound = Uri.parse( - ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + - getPackageName() + "/raw/ringer"); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setContentTitle(name) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .build(); Log.d(TAG, "n.sound=" + n.sound); mNM.notify(1, n); @@ -474,9 +470,13 @@ public class NotificationTestList extends TestActivity new Test("Sound and Cancel") { public void run() { - Notification n = new Notification(); - n.sound = Uri.parse( - "content://media/internal/audio/media/7"); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setContentTitle(name) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .build(); + Log.d(TAG, "n.sound=" + n.sound); mNM.notify(1, n); SystemClock.sleep(200); @@ -487,8 +487,11 @@ public class NotificationTestList extends TestActivity new Test("Vibrate") { public void run() { - Notification n = new Notification(); - n.vibrate = new long[] { 0, 700, 500, 1000 }; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setContentTitle(name) + .setVibrate(new long[]{0, 700, 500, 1000}) + .build(); mNM.notify(1, n); } @@ -497,8 +500,11 @@ public class NotificationTestList extends TestActivity new Test("Vibrate and cancel") { public void run() { - Notification n = new Notification(); - n.vibrate = new long[] { 0, 700, 500, 1000 }; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setContentTitle(name) + .setVibrate(new long[]{0, 700, 500, 1000}) + .build(); mNM.notify(1, n); SystemClock.sleep(500); @@ -566,10 +572,13 @@ public class NotificationTestList extends TestActivity new Test("Persistent #1") { public void run() { - Notification n = new Notification(R.drawable.icon1, "tick tick tick", - mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is a notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(1, n); } }, @@ -578,18 +587,19 @@ public class NotificationTestList extends TestActivity public void run() { mHandler.postDelayed(new Runnable() { public void run() { - Notification n = new Notification(R.drawable.icon1, - " " + String message = " " + "tick tock tick tock\n\nSometimes notifications can " + "be really long and wrap to more than one line.\n" + "Sometimes." + "Ohandwhathappensifwehaveonereallylongstringarewesure" - + "thatwesegmentitcorrectly?\n", - System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, - "Still Persistent #1", - "This is still a notification!!!", - makeIntent()); + + "thatwesegmentitcorrectly?\n"; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setContentTitle(name) + .setContentText("This is still a notification!!!") + .setContentIntent(makeIntent()) + .setStyle(new Notification.BigTextStyle().bigText(message)) + .build(); mNM.notify(1, n); } }, 3000); @@ -598,54 +608,67 @@ public class NotificationTestList extends TestActivity new Test("Persistent #2") { public void run() { - Notification n = new Notification(R.drawable.icon2, "tock tock tock", - System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #2", - "Notify me!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(2, n); } }, new Test("Persistent #3") { public void run() { - Notification n = new Notification(R.drawable.icon2, "tock tock tock\nmooooo", - System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #3", - "Notify me!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(3, n); } }, new Test("Persistent #2 Vibrate") { public void run() { - Notification n = new Notification(R.drawable.icon2, "tock tock tock", - System.currentTimeMillis()); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #2", - "Notify me!!!", makeIntent()); - n.defaults = Notification.DEFAULT_VIBRATE; + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent()) + .setDefaults(Notification.DEFAULT_VIBRATE) + .build(); mNM.notify(2, n); } }, new Test("Persistent #1 - different icon") { public void run() { - Notification n = new Notification(R.drawable.icon2, null, - mActivityCreateTime); - n.setLatestEventInfo(NotificationTestList.this, "Persistent #1", - "This is the same notification!!!", makeIntent()); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setWhen(mActivityCreateTime) + .setContentTitle(name) + .setContentText("This is a notification!!!") + .setContentIntent(makeIntent()) + .build(); mNM.notify(1, n); } }, new Test("Chronometer Start") { public void run() { - Notification n = new Notification(R.drawable.icon2, "me me me me", - System.currentTimeMillis()); - n.contentView = new RemoteViews(getPackageName(), R.layout.chrono_notification); - mChronometerBase = SystemClock.elapsedRealtime(); - n.contentView.setChronometer(R.id.time, mChronometerBase, "Yay! (%s)", true); - n.flags |= Notification.FLAG_ONGOING_EVENT; - n.contentIntent = makeIntent(); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(System.currentTimeMillis()) + .setContentTitle(name) + .setContentIntent(makeIntent()) + .setOngoing(true) + .setUsesChronometer(true) + .build(); mNM.notify(2, n); } }, @@ -655,12 +678,12 @@ public class NotificationTestList extends TestActivity mHandler.postDelayed(new Runnable() { public void run() { Log.d(TAG, "Chronometer Stop"); - Notification n = new Notification(); - n.icon = R.drawable.icon1; - n.contentView = new RemoteViews(getPackageName(), - R.layout.chrono_notification); - n.contentView.setChronometer(R.id.time, mChronometerBase, null, false); - n.contentIntent = makeIntent(); + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon1) + .setWhen(System.currentTimeMillis()) + .setContentTitle(name) + .setContentIntent(makeIntent()) + .build(); mNM.notify(2, n); } }, 3000); @@ -669,29 +692,29 @@ public class NotificationTestList extends TestActivity new Test("Sequential Persistent") { public void run() { - mNM.notify(1, notificationWithNumbers(1)); - mNM.notify(2, notificationWithNumbers(2)); + mNM.notify(1, notificationWithNumbers(name, 1)); + mNM.notify(2, notificationWithNumbers(name, 2)); } }, new Test("Replace Persistent") { public void run() { - mNM.notify(1, notificationWithNumbers(1)); - mNM.notify(1, notificationWithNumbers(1)); + mNM.notify(1, notificationWithNumbers(name, 1)); + mNM.notify(1, notificationWithNumbers(name, 1)); } }, new Test("Run and Cancel (n=1)") { public void run() { - mNM.notify(1, notificationWithNumbers(1)); + mNM.notify(1, notificationWithNumbers(name, 1)); mNM.cancel(1); } }, new Test("Run an Cancel (n=2)") { public void run() { - mNM.notify(1, notificationWithNumbers(1)); - mNM.notify(2, notificationWithNumbers(2)); + mNM.notify(1, notificationWithNumbers(name, 1)); + mNM.notify(2, notificationWithNumbers(name, 2)); mNM.cancel(2); } }, @@ -701,8 +724,8 @@ public class NotificationTestList extends TestActivity public void run() { for (int i = 0; i < 10; i++) { Log.d(TAG, "Add two notifications"); - mNM.notify(1, notificationWithNumbers(1)); - mNM.notify(2, notificationWithNumbers(2)); + mNM.notify(1, notificationWithNumbers(name, 1)); + mNM.notify(2, notificationWithNumbers(name, 2)); Log.d(TAG, "Cancel two notifications"); mNM.cancel(1); mNM.cancel(2); @@ -712,29 +735,14 @@ public class NotificationTestList extends TestActivity new Test("Ten Notifications") { public void run() { - for (int i = 0; i < 2; i++) { - Notification n = new Notification( - kNumberedIconResIDs[i], - null, System.currentTimeMillis()); - n.number = i; - n.setLatestEventInfo( - NotificationTestList.this, - "Persistent #" + i, - "Notify me!!!" + i, - null); - n.flags |= Notification.FLAG_ONGOING_EVENT; - mNM.notify((i+1)*10, n); - } - for (int i = 2; i < 10; i++) { - Notification n = new Notification( - kNumberedIconResIDs[i], - null, System.currentTimeMillis()); - n.number = i; - n.setLatestEventInfo( - NotificationTestList.this, - "Persistent #" + i, - "Notify me!!!" + i, - null); + for (int i = 0; i < 10; i++) { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(kNumberedIconResIDs[i]) + .setContentTitle("Persistent #" + i) + .setContentText("Notify me!!!" + i) + .setOngoing(i < 2) + .setNumber(i) + .build(); mNM.notify((i+1)*10, n); } } @@ -757,25 +765,25 @@ public class NotificationTestList extends TestActivity new Test("Persistent with numbers 1") { public void run() { - mNM.notify(1, notificationWithNumbers(1)); + mNM.notify(1, notificationWithNumbers(name, 1)); } }, new Test("Persistent with numbers 22") { public void run() { - mNM.notify(1, notificationWithNumbers(22)); + mNM.notify(1, notificationWithNumbers(name, 22)); } }, new Test("Persistent with numbers 333") { public void run() { - mNM.notify(1, notificationWithNumbers(333)); + mNM.notify(1, notificationWithNumbers(name, 333)); } }, new Test("Persistent with numbers 4444") { public void run() { - mNM.notify(1, notificationWithNumbers(4444)); + mNM.notify(1, notificationWithNumbers(name, 4444)); } }, @@ -786,7 +794,7 @@ public class NotificationTestList extends TestActivity .setContentTitle("High priority") .setContentText("This should appear before all others") .setPriority(Notification.PRIORITY_HIGH) - .getNotification(); + .build(); int[] idOut = new int[1]; try { @@ -812,7 +820,7 @@ public class NotificationTestList extends TestActivity .setContentTitle("MAX priority") .setContentText("This might appear as an intruder alert") .setPriority(Notification.PRIORITY_MAX) - .getNotification(); + .build(); int[] idOut = new int[1]; try { @@ -838,7 +846,7 @@ public class NotificationTestList extends TestActivity .setContentTitle("MIN priority") .setContentText("You should not see this") .setPriority(Notification.PRIORITY_MIN) - .getNotification(); + .build(); int[] idOut = new int[1]; try { @@ -875,16 +883,15 @@ public class NotificationTestList extends TestActivity }; - private Notification notificationWithNumbers(int num) { - Notification n = new Notification(this, - (num >= 0 && num < kNumberedIconResIDs.length) - ? kNumberedIconResIDs[num] - : kUnnumberedIconResID, - null, - System.currentTimeMillis(), - "Notification", "Number=" + num, - null); - n.number = num; + private Notification notificationWithNumbers(String name, int num) { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon((num >= 0 && num < kNumberedIconResIDs.length) + ? kNumberedIconResIDs[num] + : kUnnumberedIconResID) + .setContentTitle(name) + .setContentText("Number=" + num) + .setNumber(num) + .build(); return n; } @@ -932,9 +939,12 @@ public class NotificationTestList extends TestActivity } void timeNotification(int n, String label, long time) { - mNM.notify(n, new Notification(NotificationTestList.this, - R.drawable.ic_statusbar_missedcall, null, - time, label, "" + new java.util.Date(time), null)); + mNM.notify(n, new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.ic_statusbar_missedcall) + .setWhen(time) + .setContentTitle(label) + .setContentText(new java.util.Date(time).toString()) + .build()); } diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java index 50f98b8..cd04c2e 100644 --- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java +++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java @@ -153,25 +153,24 @@ public class StatusBarTest extends TestActivity }, new Test("Priority notification") { public void run() { - Notification not = new Notification(); - not.icon = R.drawable.stat_sys_phone; - not.when = System.currentTimeMillis()-(1000*60*60*24); - not.setLatestEventInfo(StatusBarTest.this, - "Incoming call", - "from: Imperious Leader", - null - ); - not.flags |= Notification.FLAG_HIGH_PRIORITY; Intent fullScreenIntent = new Intent(StatusBarTest.this, TestAlertActivity.class); int id = (int)System.currentTimeMillis(); // XXX HAX fullScreenIntent.putExtra("id", id); - not.fullScreenIntent = PendingIntent.getActivity( + PendingIntent pi = PendingIntent.getActivity( StatusBarTest.this, 0, fullScreenIntent, PendingIntent.FLAG_CANCEL_CURRENT); - // if you tap on it you should get the original alert box - not.contentIntent = not.fullScreenIntent; + Notification not = new Notification.Builder(StatusBarTest.this) + .setSmallIcon(R.drawable.stat_sys_phone) + .setWhen(System.currentTimeMillis() - (1000 * 60 * 60 * 24)) + .setContentTitle("Incoming call") + .setContentText("from: Imperious Leader") + .setContentIntent(pi) + .setFullScreenIntent(pi, true) + .setPriority(Notification.PRIORITY_HIGH) + .build(); + mNotificationManager.notify(id, not); } }, |