Merge "MediaSync: add OnErrorListener."
diff --git a/api/current.txt b/api/current.txt
index 1e0138c..2fec250 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6132,6 +6132,7 @@
}
public final class UsageStatsManager {
+ method public boolean isAppIdle(java.lang.String);
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -28447,13 +28448,6 @@
package android.security {
- public class CryptoOperationException extends java.lang.RuntimeException {
- ctor public CryptoOperationException();
- ctor public CryptoOperationException(java.lang.String);
- ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
- ctor public CryptoOperationException(java.lang.Throwable);
- }
-
public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
method public int getDemCipherKeySize();
method public java.lang.String getDemCipherTransformation();
@@ -28510,7 +28504,7 @@
ctor public KeyChainException(java.lang.Throwable);
}
- public class KeyExpiredException extends android.security.CryptoOperationException {
+ public class KeyExpiredException extends java.security.InvalidKeyException {
ctor public KeyExpiredException();
ctor public KeyExpiredException(java.lang.String);
ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -28552,7 +28546,7 @@
method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
}
- public class KeyNotYetValidException extends android.security.CryptoOperationException {
+ public class KeyNotYetValidException extends java.security.InvalidKeyException {
ctor public KeyNotYetValidException();
ctor public KeyNotYetValidException(java.lang.String);
ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -28700,12 +28694,12 @@
method public boolean isCleartextTrafficPermitted();
}
- public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+ public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
ctor public NewFingerprintEnrolledException();
ctor public NewFingerprintEnrolledException(java.lang.String);
}
- public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+ public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
ctor public UserNotAuthenticatedException();
ctor public UserNotAuthenticatedException(java.lang.String);
ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7911fd0..f1156ad 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6320,6 +6320,7 @@
}
public final class UsageStatsManager {
+ method public boolean isAppIdle(java.lang.String);
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -30451,13 +30452,6 @@
package android.security {
- public class CryptoOperationException extends java.lang.RuntimeException {
- ctor public CryptoOperationException();
- ctor public CryptoOperationException(java.lang.String);
- ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
- ctor public CryptoOperationException(java.lang.Throwable);
- }
-
public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
method public int getDemCipherKeySize();
method public java.lang.String getDemCipherTransformation();
@@ -30514,7 +30508,7 @@
ctor public KeyChainException(java.lang.Throwable);
}
- public class KeyExpiredException extends android.security.CryptoOperationException {
+ public class KeyExpiredException extends java.security.InvalidKeyException {
ctor public KeyExpiredException();
ctor public KeyExpiredException(java.lang.String);
ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -30556,7 +30550,7 @@
method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
}
- public class KeyNotYetValidException extends android.security.CryptoOperationException {
+ public class KeyNotYetValidException extends java.security.InvalidKeyException {
ctor public KeyNotYetValidException();
ctor public KeyNotYetValidException(java.lang.String);
ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -30704,12 +30698,12 @@
method public boolean isCleartextTrafficPermitted();
}
- public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+ public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
ctor public NewFingerprintEnrolledException();
ctor public NewFingerprintEnrolledException(java.lang.String);
}
- public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+ public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
ctor public UserNotAuthenticatedException();
ctor public UserNotAuthenticatedException(java.lang.String);
ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8ba2a5a..219d35b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -142,6 +142,8 @@
" am task resizeable <TASK_ID> [true|false]\n" +
" am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am get-config\n" +
+ " am set-idle [--user <USER_ID>] <PACKAGE> true|false\n" +
+ " am get-idle [--user <USER_ID>] <PACKAGE>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -282,6 +284,11 @@
"am get-config: retrieve the configuration and any recent configurations\n" +
" of the device\n" +
"\n" +
+ "am set-idle: sets the idle state of an app\n" +
+ "\n" +
+ "am get-idle: returns the idle state of an app\n" +
+ "\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@@ -388,6 +395,10 @@
runTask();
} else if (op.equals("get-config")) {
runGetConfig();
+ } else if (op.equals("set-idle")) {
+ runSetIdle();
+ } else if (op.equals("get-idle")) {
+ runGetIdle();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -2019,6 +2030,46 @@
}
}
+ private void runSetIdle() throws Exception {
+ int userId = UserHandle.USER_OWNER;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(nextArgRequired());
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return;
+ }
+ }
+ String packageName = nextArgRequired();
+ String value = nextArgRequired();
+
+ IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ usm.setAppIdle(packageName, Boolean.parseBoolean(value), userId);
+ }
+
+ private void runGetIdle() throws Exception {
+ int userId = UserHandle.USER_OWNER;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(nextArgRequired());
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return;
+ }
+ }
+ String packageName = nextArgRequired();
+
+ IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ boolean isIdle = usm.isAppIdle(packageName, userId);
+ System.out.println("Idle=" + isIdle);
+ }
+
/**
* Open the given file for sending into the system process. This verifies
* with SELinux that the system will have access to the file.
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 56cd53e..ebb3c43 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,8 @@
package android.app;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@
*
* @see #reenableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void disableKeyguard() {
try {
mWM.disableKeyguard(mToken, mTag);
@@ -132,6 +135,7 @@
*
* @see #disableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void reenableKeyguard() {
try {
mWM.reenableKeyguard(mToken);
@@ -302,6 +306,7 @@
* once the user has gotten past the keyguard.
*/
@Deprecated
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
try {
mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4ed1489..23659e3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,4 +30,6 @@
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
+ void setAppIdle(String packageName, boolean idle, int userId);
+ boolean isAppIdle(String packageName, int userId);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index bc6099a..8a01d66 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import java.util.Collections;
@@ -217,4 +218,20 @@
}
return aggregatedStats;
}
+
+ /**
+ * Returns whether the specified app is currently considered idle. This will be true if the
+ * app hasn't been used directly or indirectly for a period of time defined by the system. This
+ * could be of the order of several hours or days.
+ * @param packageName The package name of the app to query
+ * @return whether the app is currently considered idle
+ */
+ public boolean isAppIdle(String packageName) {
+ try {
+ return mService.isAppIdle(packageName, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ // fall through and return default
+ }
+ return false;
+ }
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ecdc9c..c959774 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1854,20 +1854,19 @@
moved = true;
}
break;
- case FOCUS_LEFT:
- if (selectedPosition > startOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.max(0, selectedPosition - 1));
- moved = true;
- }
- break;
- case FOCUS_RIGHT:
- if (selectedPosition < endOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
- moved = true;
- }
- break;
+ }
+
+ final boolean isLayoutRtl = isLayoutRtl();
+ if (selectedPosition > startOfRowPos && ((direction == FOCUS_LEFT && !isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.max(0, selectedPosition - 1));
+ moved = true;
+ } else if (selectedPosition < endOfRowPos && ((direction == FOCUS_LEFT && isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && !isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
+ moved = true;
}
if (moved) {
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index f7e9648..c0c8aec 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -403,7 +403,7 @@
// No longer care about configuration changes
mContext.unregisterReceiver(mConfigurationChangedReceiver);
- mWindowManager.removeView(mContainer);
+ mWindowManager.removeViewImmediate(mContainer);
mHandler.removeCallbacks(mPostedVisibleInitializer);
if (mCallback != null) {
@@ -490,7 +490,7 @@
setVisible(false);
return true;
}
-
+
} else {
dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dced051..f430eb5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2105,6 +2105,11 @@
android:protectionLevel="signature|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <!-- @hide Allows an application to change the app idle state of an app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|system|development" />
diff --git a/core/res/res/drawable/list_highlight_material.xml b/core/res/res/drawable/list_choice_background_material.xml
similarity index 100%
rename from core/res/res/drawable/list_highlight_material.xml
rename to core/res/res/drawable/list_choice_background_material.xml
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index db178fa..f81ee8c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -614,7 +614,7 @@
<style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
<style name="Widget.Material.GridView" parent="Widget.GridView">
- <item name="listSelector">?attr/selectableItemBackground</item>
+ <item name="listSelector">?attr/listChoiceBackgroundIndicator</item>
</style>
<style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a413d91..e8aab07 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -129,8 +129,8 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_highlight_material</item>
- <item name="activatedBackgroundIndicator">@null</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
+ <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
<item name="listDividerAlertDialog">@null</item>
@@ -485,7 +485,7 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
- <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
<item name="expandableListPreferredItemPaddingLeft">40dip</item>
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index d674f7f..5ccdcb9 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -74,30 +74,8 @@
<a href="http://android-developers.blogspot.com/2015/04/theres-lot-to-explore-with-google-play.html"
class="external-link">blog post</a>.</p>
<ul>
- <li><strong>Maps</strong> - This release makes the Google Maps Android API v2 available on
-<a href="https://developers.google.com/maps/documentation/android/wear" class="external-link">
-Android Wear</a>, so you can now create map-based apps that run directly on wearable devices. In
-addition, the Maps API now offers a new
-<a href="{@docRoot}reference/com/google/android/gms/maps/StreetViewPanorama.OnStreetViewPanoramaLongClickListener.html">
-{@code OnStreetViewPanoramaLongClickListener}</a> interface, similar to the existing
-<a href="{@docRoot}reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html">
-{@code OnMapLongClickListener}</a> interface. These listeners are particularly helpful
-for wearable devices, so you can let users exit from the app by long-clicking on a map or panorama.
-On a wearable device, the swipe gesture is used to pan the map instead of exiting the app.
- <ul>
- <li><a href="https://developers.google.com/maps/documentation/android/wear"
- class="external-link">Google Maps on Android Wear developer guide</a>
- </li>
- <li><a href="https://github.com/googlemaps/android-samples/tree/master/BasicWearMap"
- class="external-link">Google Maps on Android Wear sample</a>
- </li>
- <li><a href="https://developers.google.com/maps/documentation/android/releases"
- class="external-link">Release notes</a>
- </li>
- </ul>
- </li>
<li>
- <strong>Wear</strong> - In addition to Maps support, this release provides you with the ability
+ <strong>Wear</strong> - This release provides you with the ability
to advertise and discover the capabilities of devices that are connected in a Wear network, through
the new <a href="{@docRoot}reference/com/google/android/gms/wearable/CapabilityApi.html">
{@code CapabilityApi}</a> class. The new
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
deleted file mode 100644
index 1c9d005..0000000
--- a/keystore/java/android/security/CryptoOperationException.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 android.security;
-
-/**
- * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
- * exception.
- *
- * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
- * {@code Signature}) is that they can throw a checked exception during initialization, but are not
- * permitted to throw a checked exception during operation. Because crypto operations can fail
- * for a variety of reasons after initialization, this base class provides type-safety for unchecked
- * exceptions that may be thrown in those cases.
- */
-public class CryptoOperationException extends RuntimeException {
-
- /**
- * Constructs a new {@code CryptoOperationException} without detail message and cause.
- */
- public CryptoOperationException() {
- super();
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and no
- * cause.
- */
- public CryptoOperationException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
- */
- public CryptoOperationException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided cause.
- */
- public CryptoOperationException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java
index a02dc33..f58e48a 100644
--- a/keystore/java/android/security/KeyExpiredException.java
+++ b/keystore/java/android/security/KeyExpiredException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity end date
* is in the past.
*/
-public class KeyExpiredException extends CryptoOperationException {
+public class KeyExpiredException extends InvalidKeyException {
/**
* Constructs a new {@code KeyExpiredException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java
index 964cd7e..4ea27ef 100644
--- a/keystore/java/android/security/KeyNotYetValidException.java
+++ b/keystore/java/android/security/KeyNotYetValidException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity start date
* is in the future.
*/
-public class KeyNotYetValidException extends CryptoOperationException {
+public class KeyNotYetValidException extends InvalidKeyException {
/**
* Constructs a new {@code KeyNotYetValidException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 5af0527..5157932 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -30,6 +30,7 @@
import android.security.keymaster.OperationResult;
import android.util.Log;
+import java.security.InvalidKeyException;
import java.util.Locale;
/**
@@ -508,7 +509,11 @@
}
}
- public static KeyStoreException getKeyStoreException(int errorCode) {
+ /**
+ * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static KeyStoreException getKeyStoreException(int errorCode) {
if (errorCode > 0) {
// KeyStore layer error
switch (errorCode) {
@@ -544,7 +549,11 @@
}
}
- public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided
+ * {@link KeyStoreException}.
+ */
+ static InvalidKeyException getInvalidKeyException(KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
return new KeyExpiredException();
@@ -556,11 +565,15 @@
// case KeymasterDefs.KM_ERROR_TBD
// return new NewFingerprintEnrolledException();
default:
- return new CryptoOperationException("Crypto operation failed", e);
+ return new InvalidKeyException("Keystore operation failed", e);
}
}
- public static CryptoOperationException getCryptoOperationException(int errorCode) {
- return getCryptoOperationException(getKeyStoreException(errorCode));
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static InvalidKeyException getInvalidKeyException(int errorCode) {
+ return getInvalidKeyException(getKeyStoreException(errorCode));
}
}
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 1f8d8ec..3b13e83 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -136,6 +136,14 @@
private Long mOperationHandle;
private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+ /**
+ * Encountered exception which could not be immediately thrown because it was encountered inside
+ * a method that does not throw checked exception. This exception will be thrown from
+ * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
+ * {@code engineDoFinal} start ignoring input data.
+ */
+ private Exception mCachedException;
+
protected KeyStoreCipherSpi(
int keymasterAlgorithm,
int keymasterBlockMode,
@@ -158,7 +166,11 @@
try {
init(opmode, key, random);
initAlgorithmSpecificParameters();
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException(e);
+ }
success = true;
} finally {
if (!success) {
@@ -236,6 +248,7 @@
mOperationToken = null;
mOperationHandle = null;
mMainDataStreamer = null;
+ mCachedException = null;
}
private void resetWhilePreservingInitState() {
@@ -247,12 +260,17 @@
mOperationHandle = null;
mMainDataStreamer = null;
mAdditionalEntropyForBegin = null;
+ mCachedException = null;
}
- private void ensureKeystoreOperationInitialized() {
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
if (mMainDataStreamer != null) {
return;
}
+ if (mCachedException != null) {
+ return;
+ }
if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
@@ -281,11 +299,15 @@
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ switch (opResult.resultCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_NONCE:
+ throw new InvalidAlgorithmParameterException("Invalid IV");
+ }
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@@ -299,7 +321,15 @@
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ return null;
+ }
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ mCachedException = e;
+ return null;
+ }
if (inputLen == 0) {
return null;
@@ -309,7 +339,8 @@
try {
output = mMainDataStreamer.update(input, inputOffset, inputLen);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ mCachedException = e;
+ return null;
}
if (output.length == 0) {
@@ -338,7 +369,16 @@
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ throw (IllegalBlockSizeException)
+ new IllegalBlockSizeException().initCause(mCachedException);
+ }
+
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+ }
byte[] output;
try {
@@ -352,7 +392,7 @@
case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
throw new AEADBadTagException();
default:
- throw KeyStore.getCryptoOperationException(e);
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
}
}
@@ -613,11 +653,11 @@
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new CryptoOperationException("IV in use differs from provided IV");
+ throw new IllegalStateException("IV in use differs from provided IV");
}
} else {
if (returnedIv != null) {
- throw new CryptoOperationException(
+ throw new IllegalStateException(
"IV in use despite IV not being used by this transformation");
}
}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 8ed6e04..1aa3aec 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -21,7 +21,7 @@
*
* @hide
*/
-public class KeyStoreConnectException extends CryptoOperationException {
+public class KeyStoreConnectException extends IllegalStateException {
public KeyStoreConnectException() {
super("Failed to communicate with keystore service");
}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
index aafd2fa..0619199 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -136,7 +136,7 @@
// More input is available, but it wasn't included into the previous chunk
// because the chunk reached its maximum permitted size.
// Shouldn't have happened.
- throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+ throw new IllegalStateException("Nothing consumed from max-sized chunk: "
+ chunk.length + " bytes");
}
mBuffered = chunk;
@@ -148,7 +148,7 @@
mBufferedOffset = opResult.inputConsumed;
mBufferedLength = chunk.length - opResult.inputConsumed;
} else {
- throw new CryptoOperationException("Consumed more than provided: "
+ throw new IllegalStateException("Consumed more than provided: "
+ opResult.inputConsumed + ", provided: " + chunk.length);
}
@@ -160,7 +160,7 @@
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
}
} else {
@@ -173,7 +173,7 @@
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
return bufferedOutput.toByteArray();
}
@@ -233,10 +233,10 @@
}
if (opResult.inputConsumed < chunk.length) {
- throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+ throw new IllegalStateException("Keystore failed to consume all input. Provided: "
+ chunk.length + ", consumed: " + opResult.inputConsumed);
} else if (opResult.inputConsumed > chunk.length) {
- throw new CryptoOperationException("Keystore consumed more input than provided"
+ throw new IllegalStateException("Keystore consumed more input than provided"
+ " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index f8b6fef..175369c 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -147,7 +147,7 @@
resetWhilePreservingInitState();
}
- private void ensureKeystoreOperationInitialized() {
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
if (mChunkedStreamer != null) {
return;
}
@@ -169,10 +169,10 @@
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@@ -188,28 +188,36 @@
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] output;
try {
output = mChunkedStreamer.update(input, offset, len);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
if ((output != null) && (output.length != 0)) {
- throw new CryptoOperationException("Update operation unexpectedly produced output");
+ throw new IllegalStateException("Update operation unexpectedly produced output");
}
}
@Override
protected byte[] engineDoFinal() {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] result;
try {
result = mChunkedStreamer.doFinal(null, 0, 0);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
resetWhilePreservingInitState();
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index d1abe12..293c4c9 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -210,7 +210,8 @@
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(errorCode);
+ throw new IllegalStateException(
+ "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
String keyAlgorithmJCA =
KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java
index 806b214..4fe210b 100644
--- a/keystore/java/android/security/NewFingerprintEnrolledException.java
+++ b/keystore/java/android/security/NewFingerprintEnrolledException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation could not be performed because the key used by the
* operation is permanently invalid because a new fingerprint was enrolled.
*/
-public class NewFingerprintEnrolledException extends CryptoOperationException {
+public class NewFingerprintEnrolledException extends InvalidKeyException {
/**
* Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause.
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
index f5f5f41..66f4dd8 100644
--- a/keystore/java/android/security/UserNotAuthenticatedException.java
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation could not be performed because the user has not been
* authenticated recently enough.
*/
-public class UserNotAuthenticatedException extends CryptoOperationException {
+public class UserNotAuthenticatedException extends InvalidKeyException {
/**
* Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 2346abe..201a796 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -59,7 +59,7 @@
/** Minimum value for sample rate */
private static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 96000;
+ private static final int SAMPLE_RATE_HZ_MAX = 192000;
/**
* indicates AudioRecord state is not successfully initialized.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 847bdc2..3577357 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -96,7 +96,7 @@
/** Minimum value for sample rate */
private static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 96000;
+ private static final int SAMPLE_RATE_HZ_MAX = 192000;
/** Maximum value for AudioTrack channel count */
private static final int CHANNEL_COUNT_MAX = 8;
@@ -1448,7 +1448,8 @@
* the position values have different meanings.
* <br>
* If looping is currently enabled and the new position is greater than or equal to the
- * loop end marker, the behavior varies by API level: for API level 22 and above,
+ * loop end marker, the behavior varies by API level:
+ * as of {@link android.os.Build.VERSION_CODES#MNC},
* the looping is first disabled and then the position is set.
* For earlier API levels, the behavior is unspecified.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -1485,7 +1486,7 @@
* {@link #ERROR_BAD_VALUE} is returned.
* The loop range is the interval [startInFrames, endInFrames).
* <br>
- * For API level 22 and above, the position is left unchanged,
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, the position is left unchanged,
* unless it is greater than or equal to the loop end marker, in which case
* it is forced to the loop start marker.
* For earlier API levels, the effect on position is unspecified.
@@ -2032,12 +2033,12 @@
* The track must be stopped or paused, and
* the track's creation mode must be {@link #MODE_STATIC}.
* <p>
- * For API level 22 and above, also resets the value returned by
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, also resets the value returned by
* {@link #getPlaybackHeadPosition()} to zero.
* For earlier API levels, the reset behavior is unspecified.
* <p>
- * {@link #setPlaybackHeadPosition(int)} to zero
- * is recommended instead when the reset of {@link #getPlaybackHeadPosition} is not needed.
+ * Use {@link #setPlaybackHeadPosition(int)} with a zero position
+ * if the reset of <code>getPlaybackHeadPosition()</code> is not needed.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index e0af29d..1cf7248 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageVolume;
@@ -76,6 +77,9 @@
Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
public static class MeasurementDetails {
+ public long totalSize;
+ public long availSize;
+
/**
* Total apps disk usage.
* <p>
@@ -121,7 +125,7 @@
}
public interface MeasurementReceiver {
- public void onDetailsChanged(MeasurementDetails details);
+ void onDetailsChanged(MeasurementDetails details);
}
private WeakReference<MeasurementReceiver> mReceiver;
@@ -370,6 +374,10 @@
}
}
+ final File file = mVolume.getPath();
+ details.totalSize = file.getTotalSpace();
+ details.availSize = file.getFreeSpace();
+
// Measure all apps hosted on this volume for all users
if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 35e9636..5b4b4fd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -94,6 +94,7 @@
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 292c9c2..0d331d1 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -328,7 +328,7 @@
int fillColor = getFillColor(darkIntensity);
mIconTint = fillColor;
mFramePaint.setColor(backgroundColor);
- mBoltPaint.setColor(backgroundColor);
+ mBoltPaint.setColor(fillColor);
mChargeColor = fillColor;
invalidate();
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 89a7173..81088c4 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1758,6 +1758,9 @@
@Override
public void finishMediaUpdate() {
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to call finishMediaUpdate()");
+ }
if (mUnmountSignal != null) {
mUnmountSignal.countDown();
} else {
@@ -1791,7 +1794,7 @@
warnOnNotMounted();
final ObbState state;
- synchronized (mObbPathToStateMap) {
+ synchronized (mObbMounts) {
state = mObbPathToStateMap.get(rawPath);
}
if (state == null) {
@@ -1843,7 +1846,7 @@
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
final ObbState existingState;
- synchronized (mObbPathToStateMap) {
+ synchronized (mObbMounts) {
existingState = mObbPathToStateMap.get(rawPath);
}
@@ -2093,6 +2096,8 @@
@Override
public String getPassword() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+ "only keyguard can retrieve password");
if (!isReady()) {
return new String();
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1b32f57..999e91b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -734,12 +734,15 @@
throw new IllegalArgumentException("account is null");
}
checkAuthenticateAccountsPermission(account);
-
- final UserAccounts accounts = getUserAccountsForCaller();
int userId = Binder.getCallingUserHandle().getIdentifier();
if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
return false;
}
+ return updateLastAuthenticatedTime(account);
+ }
+
+ private boolean updateLastAuthenticatedTime(Account account) {
+ final UserAccounts accounts = getUserAccountsForCaller();
synchronized (accounts.cacheLock) {
final ContentValues values = new ContentValues();
values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
@@ -2022,7 +2025,7 @@
try {
new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */, account.name,
- true /* authDetailsRequired */) {
+ true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
@Override
public void run() throws RemoteException {
mAuthenticator.confirmCredentials(this, account, options);
@@ -2059,7 +2062,7 @@
try {
new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */, account.name,
- false /* authDetailsRequired */) {
+ false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
@Override
public void run() throws RemoteException {
mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2492,6 +2495,11 @@
final String mAccountName;
// Indicates if we need to add auth details(like last credential time)
final boolean mAuthDetailsRequired;
+ // If set, we need to update the last authenticated time. This is
+ // currently
+ // used on
+ // successful confirming credentials.
+ final boolean mUpdateLastAuthenticatedTime;
public int mNumResults = 0;
private int mNumRequestContinued = 0;
@@ -2505,6 +2513,13 @@
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
boolean authDetailsRequired) {
+ this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
+ accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
+ }
+
+ public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
+ boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+ boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
super();
//if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2516,6 +2531,7 @@
mCreationTime = SystemClock.elapsedRealtime();
mAccountName = accountName;
mAuthDetailsRequired = authDetailsRequired;
+ mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
synchronized (mSessions) {
mSessions.put(toString(), this);
@@ -2651,15 +2667,55 @@
public void onResult(Bundle result) {
mNumResults++;
Intent intent = null;
- if (result != null && mAuthDetailsRequired) {
- long lastAuthenticatedTime = DatabaseUtils.longForQuery(
- mAccounts.openHelper.getReadableDatabase(),
- "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
- TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
- + ACCOUNTS_TYPE + "=?",
- new String[]{mAccountName, mAccountType});
- result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
- lastAuthenticatedTime);
+ if (result != null) {
+ boolean isSuccessfulConfirmCreds = result.getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT, false);
+ boolean isSuccessfulUpdateCreds =
+ result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
+ && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
+ // We should only update lastAuthenticated time, if
+ // mUpdateLastAuthenticatedTime is true and the confirmRequest
+ // or updateRequest was successful
+ boolean needUpdate = mUpdateLastAuthenticatedTime
+ && (isSuccessfulConfirmCreds || isSuccessfulUpdateCreds);
+ if (needUpdate || mAuthDetailsRequired) {
+ boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
+ if (needUpdate && accountPresent) {
+ updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
+ }
+ if (mAuthDetailsRequired) {
+ long lastAuthenticatedTime = -1;
+ if (accountPresent) {
+ lastAuthenticatedTime = DatabaseUtils.longForQuery(
+ mAccounts.openHelper.getReadableDatabase(),
+ "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " from " +
+ TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ + ACCOUNTS_TYPE + "=?",
+ new String[] {
+ mAccountName, mAccountType
+ });
+ }
+ result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+ lastAuthenticatedTime);
+ }
+ }
+ if (mAuthDetailsRequired) {
+ long lastAuthenticatedTime = -1;
+ if (isAccountPresentForCaller(mAccountName, mAccountType)) {
+ lastAuthenticatedTime = DatabaseUtils.longForQuery(
+ mAccounts.openHelper.getReadableDatabase(),
+ "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from "
+ +
+ TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ + ACCOUNTS_TYPE + "=?",
+ new String[] {
+ mAccountName, mAccountType
+ });
+ }
+ result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+ lastAuthenticatedTime);
+ }
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
@@ -3202,6 +3258,17 @@
return false;
}
+ private boolean isAccountPresentForCaller(String accountName, String accountType) {
+ if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
+ for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
+ if (account.name.equals(accountName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == Process.SYSTEM_UID) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3d54dfb..edeeaba 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -17,7 +17,10 @@
package com.android.server.usage;
import android.Manifest;
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
@@ -30,6 +33,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
@@ -82,6 +86,7 @@
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_RESET_LAST_TIMESTAMP = 4;
private final Object mLock = new Object();
Handler mHandler;
@@ -279,6 +284,29 @@
}
/**
+ * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
+ * last used timestamp to a point in time thats behind the threshold for idle.
+ */
+ void resetLastTimestamp(String packageName, int userId, boolean idle) {
+ synchronized (mLock) {
+ final long timeNow = checkAndGetTimeLocked();
+ final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0);
+
+ final UserUsageStatsService service =
+ getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ final long lastUsed = service.getLastPackageAccessTime(packageName);
+ final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
+ service.setLastTimestamp(packageName, lastTimestamp);
+ // Inform listeners if necessary
+ if (previouslyIdle != idle) {
+ // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ idle ? 1 : 0, packageName));
+ }
+ }
+ }
+
+ /**
* Called by the Binder stub.
*/
void flushToDisk() {
@@ -384,13 +412,41 @@
}
boolean isAppIdle(String packageName, int userId) {
+ if (packageName == null) return false;
if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
return false;
}
+ if (isActiveDeviceAdmin(packageName, userId)) {
+ return false;
+ }
+
final long lastUsed = getLastPackageAccessTime(packageName, userId);
return hasPassedIdleDuration(lastUsed);
}
+ void setAppIdle(String packageName, boolean idle, int userId) {
+ if (packageName == null) return;
+
+ mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ private boolean isActiveDeviceAdmin(String packageName, int userId) {
+ DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+ if (dpm == null) return false;
+ List<ComponentName> components = dpm.getActiveAdminsAsUser(userId);
+ if (components == null) {
+ return false;
+ }
+ final int size = components.size();
+ for (int i = 0; i < size; i++) {
+ if (components.get(i).getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void informListeners(String packageName, int userId, boolean isIdle) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, isIdle);
@@ -459,6 +515,10 @@
informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
+ case MSG_RESET_LAST_TIMESTAMP:
+ resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -566,6 +626,46 @@
}
@Override
+ public boolean isAppIdle(String packageName, int userId) {
+ try {
+ userId = ActivityManagerNative.getDefault().handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "isAppIdle", null);
+ } catch (RemoteException re) {
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return UsageStatsService.this.isAppIdle(packageName, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setAppIdle(String packageName, boolean idle, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManagerNative.getDefault().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, true,
+ "setAppIdle", null);
+ } catch (RemoteException re) {
+ return;
+ }
+ getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
+ "No permission to change app idle state");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ PackageInfo pi = AppGlobals.getPackageManager()
+ .getPackageInfo(packageName, 0, userId);
+ if (pi == null) return;
+ UsageStatsService.this.setAppIdle(packageName, idle, userId);
+ } catch (RemoteException re) {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0a9481a..d94759d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -211,6 +211,17 @@
notifyStatsChanged();
}
+ /**
+ * Sets the last timestamp for each of the intervals.
+ * @param lastTimestamp
+ */
+ void setLastTimestamp(String packageName, long lastTimestamp) {
+ for (IntervalStats stats : mCurrentStats) {
+ stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE);
+ }
+ notifyStatsChanged();
+ }
+
private static final StatCombiner<UsageStats> sUsageStatsCombiner =
new StatCombiner<UsageStats>() {
@Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d1a2fa..831a194 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -208,13 +208,13 @@
}
/**
- * Returns a bundle with the default value for every supported configuration variable.
+ * Returns a new bundle with the default value for every supported configuration variable.
*
* @hide
*/
@SystemApi
public static Bundle getDefaultConfig() {
- return sDefaults;
+ return new Bundle(sDefaults);
}
/** @hide */