Merge "Do not accept an empty string in EditorInfo#packageName anymore." into mnc-dev
diff --git a/Android.mk b/Android.mk
index 68f471a..1752caf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -451,6 +451,7 @@
frameworks/base/telephony/java/android/telephony/SignalStrength.aidl \
frameworks/base/telephony/java/android/telephony/IccOpenLogicalChannelResponse.aidl \
frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
+ frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
frameworks/base/location/java/android/location/Location.aidl \
frameworks/base/location/java/android/location/Address.aidl \
frameworks/base/location/java/android/location/Criteria.aidl \
diff --git a/api/current.txt b/api/current.txt
index 6b261d8..32af4c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5790,7 +5790,6 @@
method public void setPermissionPolicy(android.content.ComponentName, int);
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
- method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
method public void setProfileEnabled(android.content.ComponentName);
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -13942,16 +13941,8 @@
package android.hardware.fingerprint {
- public final class Fingerprint implements android.os.Parcelable {
- ctor public Fingerprint(java.lang.CharSequence, int, int, long);
- method public int describeContents();
- method public java.lang.CharSequence getName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
- }
-
public class FingerprintManager {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+ method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
method public boolean hasEnrolledFingerprints();
method public boolean isHardwareDetected();
field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -13960,14 +13951,12 @@
field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
- field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
- field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
}
public static abstract class FingerprintManager.AuthenticationCallback {
@@ -13979,11 +13968,10 @@
}
public static final class FingerprintManager.AuthenticationResult {
- ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
}
- public static class FingerprintManager.CryptoObject {
+ public static final class FingerprintManager.CryptoObject {
ctor public FingerprintManager.CryptoObject(java.security.Signature);
ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
@@ -15254,6 +15242,7 @@
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
field public static final java.lang.String TAG_DATETIME = "DateTime";
+ field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
field public static final java.lang.String TAG_FLASH = "Flash";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
@@ -15272,6 +15261,9 @@
field public static final java.lang.String TAG_MAKE = "Make";
field public static final java.lang.String TAG_MODEL = "Model";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
@@ -18340,6 +18332,7 @@
public class Network implements android.os.Parcelable {
method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
method public void bindSocket(java.net.Socket) throws java.io.IOException;
+ method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
method public int describeContents();
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
@@ -22910,6 +22903,8 @@
public static class Debug.MemoryInfo implements android.os.Parcelable {
ctor public Debug.MemoryInfo();
method public int describeContents();
+ method public java.lang.String getMemoryStat(java.lang.String);
+ method public java.util.Map<java.lang.String, java.lang.String> getMemoryStats();
method public int getTotalPrivateClean();
method public int getTotalPrivateDirty();
method public int getTotalPss();
@@ -23456,6 +23451,7 @@
method public static final int getGidForName(java.lang.String);
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
+ method public static final boolean is64Bit();
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -28395,7 +28391,7 @@
method public static android.content.Intent createInstallIntent();
method public static java.security.cert.X509Certificate[] getCertificateChain(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
method public static java.security.PrivateKey getPrivateKey(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
- method public static boolean isBoundKeyAlgorithm(java.lang.String);
+ method public static deprecated boolean isBoundKeyAlgorithm(java.lang.String);
method public static boolean isKeyAlgorithmSupported(java.lang.String);
field public static final java.lang.String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
field public static final java.lang.String EXTRA_CERTIFICATE = "CERT";
@@ -30614,8 +30610,8 @@
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+ field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
- field public static final java.lang.String EXTRA_INCOMING_CALL_HANDLE = "android.telecom.extra.INCOMING_CALL_HANDLE";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -30670,7 +30666,7 @@
public class CarrierConfigManager {
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
- method public void reloadCarrierConfigForSubId(int);
+ method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
diff --git a/api/system-current.txt b/api/system-current.txt
index 1c524c1..cff5c99 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5900,7 +5900,6 @@
method public void setPermissionPolicy(android.content.ComponentName, int);
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
- method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
method public void setProfileEnabled(android.content.ComponentName);
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -14260,16 +14259,8 @@
package android.hardware.fingerprint {
- public final class Fingerprint implements android.os.Parcelable {
- ctor public Fingerprint(java.lang.CharSequence, int, int, long);
- method public int describeContents();
- method public java.lang.CharSequence getName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
- }
-
public class FingerprintManager {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+ method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
method public boolean hasEnrolledFingerprints();
method public boolean isHardwareDetected();
field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -14278,14 +14269,12 @@
field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
- field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
- field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
}
public static abstract class FingerprintManager.AuthenticationCallback {
@@ -14297,11 +14286,10 @@
}
public static final class FingerprintManager.AuthenticationResult {
- ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
}
- public static class FingerprintManager.CryptoObject {
+ public static final class FingerprintManager.CryptoObject {
ctor public FingerprintManager.CryptoObject(java.security.Signature);
ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
@@ -16495,6 +16483,7 @@
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
field public static final java.lang.String TAG_DATETIME = "DateTime";
+ field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
field public static final java.lang.String TAG_FLASH = "Flash";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
@@ -16513,6 +16502,9 @@
field public static final java.lang.String TAG_MAKE = "Make";
field public static final java.lang.String TAG_MODEL = "Model";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
@@ -19826,6 +19818,7 @@
public class Network implements android.os.Parcelable {
method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
method public void bindSocket(java.net.Socket) throws java.io.IOException;
+ method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
method public int describeContents();
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
@@ -24828,6 +24821,8 @@
public static class Debug.MemoryInfo implements android.os.Parcelable {
ctor public Debug.MemoryInfo();
method public int describeContents();
+ method public java.lang.String getMemoryStat(java.lang.String);
+ method public java.util.Map<java.lang.String, java.lang.String> getMemoryStats();
method public int getTotalPrivateClean();
method public int getTotalPrivateDirty();
method public int getTotalPss();
@@ -25382,6 +25377,7 @@
method public static final int getGidForName(java.lang.String);
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
+ method public static final boolean is64Bit();
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -30428,7 +30424,7 @@
method public static android.content.Intent createInstallIntent();
method public static java.security.cert.X509Certificate[] getCertificateChain(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
method public static java.security.PrivateKey getPrivateKey(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
- method public static boolean isBoundKeyAlgorithm(java.lang.String);
+ method public static deprecated boolean isBoundKeyAlgorithm(java.lang.String);
method public static boolean isKeyAlgorithmSupported(java.lang.String);
field public static final java.lang.String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
field public static final java.lang.String EXTRA_CERTIFICATE = "CERT";
@@ -32828,8 +32824,8 @@
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
- field public static final java.lang.String EXTRA_INCOMING_CALL_HANDLE = "android.telecom.extra.INCOMING_CALL_HANDLE";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -32885,7 +32881,7 @@
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
method public static android.os.PersistableBundle getDefaultConfig();
- method public void reloadCarrierConfigForSubId(int);
+ method public void notifyConfigChangedForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
@@ -33422,6 +33418,7 @@
method public void call(java.lang.String, java.lang.String);
method public boolean canChangeDtmfToneLength();
method public int checkCarrierPrivilegesForPackage(java.lang.String);
+ method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
method public void dial(java.lang.String);
method public boolean disableDataConnectivity();
method public boolean enableDataConnectivity();
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 435d5ab..d8d2737 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -441,8 +441,12 @@
long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
- int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_FLOAT);
+ int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_UNDEFINED);
+ if (valueType == VALUE_TYPE_UNDEFINED) {
+ valueType = inferValueTypeFromValues(arrayAnimator, R.styleable.Animator_valueFrom,
+ R.styleable.Animator_valueTo);
+ }
PropertyValuesHolder pvh = getPVH(arrayAnimator, valueType,
R.styleable.Animator_valueFrom, R.styleable.Animator_valueTo, "");
if (pvh != null) {
@@ -520,8 +524,14 @@
ObjectAnimator oa = (ObjectAnimator) anim;
String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
- // Note that if there is a pathData defined in the Object Animator,
- // valueFrom / valueTo will be ignored.
+ // Path can be involved in an ObjectAnimator in the following 3 ways:
+ // 1) Path morphing: the property to be animated is pathData, and valueFrom and valueTo
+ // are both of pathType. valueType = pathType needs to be explicitly defined.
+ // 2) A property in X or Y dimension can be animated along a path: the property needs to be
+ // defined in propertyXName or propertyYName attribute, the path will be defined in the
+ // pathData attribute. valueFrom and valueTo will not be necessary for this animation.
+ // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
+ // Here we are dealing with case 2:
if (pathData != null) {
String propertyXName =
arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
@@ -805,6 +815,25 @@
return valueType;
}
+ private static int inferValueTypeFromValues(TypedArray styledAttributes, int valueFromId,
+ int valueToId) {
+ TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
+ boolean hasFrom = (tvFrom != null);
+ int fromType = hasFrom ? tvFrom.type : 0;
+ TypedValue tvTo = styledAttributes.peekValue(valueToId);
+ boolean hasTo = (tvTo != null);
+ int toType = hasTo ? tvTo.type : 0;
+
+ int valueType;
+ // Check whether it's color type. If not, fall back to default type (i.e. float type)
+ if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
+ valueType = VALUE_TYPE_COLOR;
+ } else {
+ valueType = VALUE_TYPE_FLOAT;
+ }
+ return valueType;
+ }
+
private static void dumpKeyframes(Object[] keyframes, String header) {
if (keyframes == null || keyframes.length == 0) {
return;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb553e4..dabcc50c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2534,15 +2534,6 @@
return true;
}
- case UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- ComponentName preferredActivity = ComponentName.readFromParcel(data);
- int userId = data.readInt();
- updatePreferredSetupActivity(preferredActivity, userId);
- reply.writeNoException();
- return true;
- }
-
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -5868,20 +5859,6 @@
}
@Override
- public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- ComponentName.writeToParcel(preferredActivity, data);
- data.writeInt(userId);
- mRemote.transact(UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
- @Override
public int getPackageProcessState(String packageName) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e87eabe..0d5e1c7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -502,8 +502,6 @@
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
public void updateDeviceOwner(String packageName) throws RemoteException;
- public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId)
- throws RemoteException;
public int getPackageProcessState(String packageName) throws RemoteException;
@@ -850,8 +848,7 @@
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
- int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
- int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
- int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
- int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299;
+ int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
+ int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
+ int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 9ba6a8e..39cd3bc 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -239,9 +239,10 @@
public void run() {
InputStream in = null;
OutputStream out = null;
+ java.lang.Process process = null;
try {
- java.lang.Process process = Runtime.getRuntime().exec(command);
+ process = Runtime.getRuntime().exec(command);
in = process.getInputStream();
out = new FileOutputStream(sink.getFileDescriptor());
@@ -257,7 +258,9 @@
} catch (IOException ioe) {
throw new RuntimeException("Error running shell command", ioe);
} finally {
- IoUtils.closeQuietly(in);
+ if (process != null) {
+ process.destroy();
+ }
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(sink);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55eaf27..3ab0e01 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4404,24 +4404,6 @@
}
/**
- * Called by a device initializer to set the activity to be launched on device boot or after a
- * user switch during user setup. This activity will be started regardless of the priority of
- * other 'home' activities. Once user setup is complete, the preferred setup activity will be
- * ignored.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param activity The Activity to be started by default during user setup.
- */
- public void setPreferredSetupActivity(@NonNull ComponentName admin,
- @NonNull ComponentName activity) {
- try {
- mService.setPreferredSetupActivity(admin, activity);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed talking with device policy service", re);
- }
- }
-
- /**
* Called by profile or device owners to set the default response for future runtime permission
* requests by applications. The policy can allow for normal operation which prompts the
* user to grant a permission, or can allow automatic granting or denying of runtime
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 477a3384..8c7b20a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -218,8 +218,6 @@
String getDeviceInitializer();
ComponentName getDeviceInitializerComponent();
- void setPreferredSetupActivity(in ComponentName admin, in ComponentName activity);
-
void setUserIcon(in ComponentName admin, in Bitmap icon);
void sendDeviceInitializerStatus(int statusCode, String description);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index caf21d5..e61813c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,32 +18,30 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManagerNative;
-import android.content.ContentResolver;
import android.content.Context;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
import java.security.Signature;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.Mac;
+import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+
/**
* A class that coordinates access to the fingerprint hardware.
* <p>
@@ -57,9 +55,10 @@
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
- private static final int MSG_AUTHENTICATED = 102;
- private static final int MSG_ERROR = 103;
- private static final int MSG_REMOVED = 104;
+ private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+ private static final int MSG_AUTHENTICATION_FAILED = 103;
+ private static final int MSG_ERROR = 104;
+ private static final int MSG_REMOVED = 105;
//
// Error messages from fingerprint hardware during initilization, enrollment, authentication or
@@ -112,6 +111,7 @@
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
*/
public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
@@ -162,6 +162,7 @@
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
*/
public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
@@ -173,6 +174,7 @@
private RemovalCallback mRemovalCallback;
private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
+ private Handler mHandler;
private class OnEnrollCancelListener implements OnCancelListener {
@Override
@@ -198,72 +200,71 @@
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
- public static class CryptoObject {
+ public static final class CryptoObject {
public CryptoObject(@NonNull Signature signature) {
- mSignature = signature;
- mCipher = null;
- mMac = null;
+ mCrypto = signature;
}
public CryptoObject(@NonNull Cipher cipher) {
- mCipher = cipher;
- mSignature = null;
- mMac = null;
+ mCrypto = cipher;
}
public CryptoObject(@NonNull Mac mac) {
- mMac = mac;
- mCipher = null;
- mSignature = null;
+ mCrypto = mac;
}
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
- public Signature getSignature() { return mSignature; }
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
/**
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
- public Cipher getCipher() { return mCipher; }
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
/**
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
- public Mac getMac() { return mMac; }
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
/**
* @hide
* @return the opId associated with this object or 0 if none
*/
public long getOpId() {
- if (mSignature != null) {
- return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mSignature);
- } else if (mCipher != null) {
- return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCipher);
- } else if (mMac != null) {
- return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mMac);
- }
- return 0;
+ return mCrypto != null ?
+ AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
}
- private final Signature mSignature;
- private final Cipher mCipher;
- private final Mac mMac;
+ private final Object mCrypto;
};
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, AuthenticationCallback, int)}.
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
*/
public static final class AuthenticationResult {
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
+ /**
+ * Authentication result
+ *
+ * @param crypto the crypto object
+ * @param fingerprint the recognized fingerprint data, if allowed.
+ * @hide
+ */
public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
@@ -272,7 +273,7 @@
/**
* Obtain the crypto object associated with this transaction
* @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, AuthenticationCallback, int)}.
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
*/
public CryptoObject getCryptoObject() { return mCryptoObject; }
@@ -287,28 +288,28 @@
/**
* Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, AuthenticationCallback, int)}. Users of {@link
+ * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
* FingerprintManager#authenticate(CryptoObject, CancellationSignal,
- * AuthenticationCallback, int) } must provide an implementation of this for listening to
+ * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
*/
public static abstract class AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
- * @param errMsgId An integer identifying the error message
+ * @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
- public void onAuthenticationError(int errMsgId, CharSequence errString) { }
+ public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
* Called when a recoverable error has been encountered during authentication. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
- * @param helpMsgId An integer identifying the error message
+ * @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
- public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
* Called when a fingerprint is recognized.
@@ -326,7 +327,7 @@
* Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
* CancellationSignal, int). Users of {@link #FingerprintManager()}
* must provide an implementation of this to {@link FingerprintManager#enroll(long,
- * CancellationSignal, EnrollmentCallback, int) for listening to fingerprint events.
+ * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
*
* @hide
*/
@@ -392,31 +393,35 @@
*
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
- * @param callback an object to receive authentication events
* @param flags optional flags; should be 0
+ * @param callback an object to receive authentication events
+ * @param handler an optional handler to handle callback events
*/
+ @RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, int flags) {
- authenticate(crypto, cancel, callback, flags, UserHandle.myUserId());
+ int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+ authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
}
/**
- * Request authentication of a crypto object. This call warms up the fingerprint hardware
- * and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
- * which point the object is no longer valid. The operation can be canceled by using the
- * provided cancel object.
- *
- * @param crypto object associated with the call or null if none required.
- * @param cancel an object that can be used to cancel authentication
- * @param callback an object to receive authentication events
- * @param flags optional flags; should be 0
- * @param userId the userId the fingerprint belongs to
+ * Use the provided handler thread for events.
+ * @param handler
+ */
+ private void useHandler(Handler handler) {
+ if (handler != null) {
+ mHandler = new MyHandler(handler.getLooper());
+ } else if (mHandler.getLooper() != mContext.getMainLooper()){
+ mHandler = new MyHandler(mContext.getMainLooper());
+ }
+ }
+
+ /**
+ * Per-user version
* @hide
*/
+ @RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, int flags, int userId) {
+ int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
@@ -431,6 +436,7 @@
}
if (mService != null) try {
+ useHandler(handler);
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
@@ -458,12 +464,13 @@
* @param token a unique token provided by a recent creation or verification of device
* credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
- * @param callback an object to receive enrollment events
* @param flags optional flags
+ * @param callback an object to receive enrollment events
* @hide
*/
- public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback,
- int flags) {
+ @RequiresPermission(MANAGE_FINGERPRINT)
+ public void enroll(byte [] token, CancellationSignal cancel, int flags,
+ EnrollmentCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -496,6 +503,7 @@
* existing device credentials (e.g. pin/pattern/password).
* @hide
*/
+ @RequiresPermission(MANAGE_FINGERPRINT)
public long preEnroll() {
long result = 0;
if (mService != null) try {
@@ -514,6 +522,7 @@
*
* @hide
*/
+ @RequiresPermission(MANAGE_FINGERPRINT)
public void remove(Fingerprint fp, RemovalCallback callback) {
if (mService != null) try {
mRemovalCallback = callback;
@@ -535,6 +544,7 @@
*
* @hide
*/
+ @RequiresPermission(MANAGE_FINGERPRINT)
public void rename(int fpId, String newName) {
// Renames the given fpId
if (mService != null) {
@@ -554,6 +564,7 @@
*
* @hide
*/
+ @RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints(int userId) {
if (mService != null) try {
return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
@@ -569,6 +580,7 @@
*
* @hide
*/
+ @RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints() {
return getEnrolledFingerprints(UserHandle.myUserId());
}
@@ -578,6 +590,7 @@
*
* @return true if at least one fingerprint is enrolled, false otherwise
*/
+ @RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
@@ -593,6 +606,7 @@
*
* @return true if hardware is present and functional, false otherwise.
*/
+ @RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
try {
@@ -626,13 +640,15 @@
return 0;
}
- private Handler mHandler;
-
private class MyHandler extends Handler {
private MyHandler(Context context) {
super(context.getMainLooper());
}
+ private MyHandler(Looper looper) {
+ super(looper);
+ }
+
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case MSG_ENROLL_RESULT:
@@ -641,8 +657,11 @@
case MSG_ACQUIRED:
sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
break;
- case MSG_AUTHENTICATED:
- sendAuthenticatedResult((Fingerprint) msg.obj);
+ case MSG_AUTHENTICATION_SUCCEEDED:
+ sendAuthenticatedSucceeded((Fingerprint) msg.obj);
+ break;
+ case MSG_AUTHENTICATION_FAILED:
+ sendAuthenticatedFailed();
break;
case MSG_ERROR:
sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
@@ -684,15 +703,16 @@
}
}
- private void sendAuthenticatedResult(Fingerprint fp) {
+ private void sendAuthenticatedSucceeded(Fingerprint fp) {
if (mAuthenticationCallback != null) {
- if (fp.getFingerId() == 0) {
- // Fingerprint template valid but doesn't match one in database
- mAuthenticationCallback.onAuthenticationFailed();
- } else {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
- mAuthenticationCallback.onAuthenticationSucceeded(result);
- }
+ final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
+ }
+
+ private void sendAuthenticatedFailed() {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationFailed();
}
}
@@ -809,24 +829,33 @@
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+ @Override // binder call
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
+ @Override // binder call
public void onAcquired(long deviceId, int acquireInfo) {
mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
}
- public void onAuthenticated(long deviceId, int fingerId, int groupId) {
- mHandler.obtainMessage(MSG_AUTHENTICATED,
- new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+ @Override // binder call
+ public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
}
+ @Override // binder call
+ public void onAuthenticationFailed(long deviceId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
+ }
+
+ @Override // binder call
public void onError(long deviceId, int error) {
mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
}
+ @Override // binder call
public void onRemoved(long deviceId, int fingerId, int groupId) {
mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index a2d74b8d..57a429f 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.fingerprint.Fingerprint;
import android.os.Bundle;
import android.os.UserHandle;
@@ -25,7 +26,8 @@
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
- void onAuthenticated(long deviceId, int fingerId, int groupId);
+ void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
+ void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 754c6b3..9628bae 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,6 +19,8 @@
import android.os.Parcelable;
import android.os.Parcel;
import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -64,7 +66,7 @@
// maybeInitHttpClient() must be called prior to reading either variable.
private volatile ConnectionPool mConnectionPool = null;
private volatile com.android.okhttp.internal.Network mNetwork = null;
- private Object mLock = new Object();
+ private final Object mLock = new Object();
// Default connection pool values. These are evaluated at startup, just
// like the OkHttp code. Also like the OkHttp code, we will throw parse
@@ -300,14 +302,10 @@
* connected.
*/
public void bindSocket(DatagramSocket socket) throws IOException {
- // Apparently, the kernel doesn't update a connected UDP socket's routing upon mark changes.
- if (socket.isConnected()) {
- throw new SocketException("Socket is connected");
- }
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocketFd(socket.getFileDescriptor$());
+ bindSocket(socket.getFileDescriptor$());
}
/**
@@ -316,18 +314,38 @@
* {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
*/
public void bindSocket(Socket socket) throws IOException {
- // Apparently, the kernel doesn't update a connected TCP socket's routing upon mark changes.
- if (socket.isConnected()) {
- throw new SocketException("Socket is connected");
- }
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocketFd(socket.getFileDescriptor$());
+ bindSocket(socket.getFileDescriptor$());
}
- private void bindSocketFd(FileDescriptor fd) throws IOException {
- int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ /**
+ * Binds the specified {@link FileDescriptor} to this {@code Network}. All data traffic on the
+ * socket represented by this file descriptor will be sent on this {@code Network},
+ * irrespective of any process-wide network binding set by
+ * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
+ */
+ public void bindSocket(FileDescriptor fd) throws IOException {
+ try {
+ final SocketAddress peer = Os.getpeername(fd);
+ final InetAddress inetPeer = ((InetSocketAddress) peer).getAddress();
+ if (!inetPeer.isAnyLocalAddress()) {
+ // Apparently, the kernel doesn't update a connected UDP socket's
+ // routing upon mark changes.
+ throw new SocketException("Socket is connected");
+ }
+ } catch (ErrnoException e) {
+ // getpeername() failed.
+ if (e.errno != OsConstants.ENOTCONN) {
+ throw e.rethrowAsSocketException();
+ }
+ } catch (ClassCastException e) {
+ // Wasn't an InetSocketAddress.
+ throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
+ }
+
+ final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 19c8fa9d..87e8c5e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -34,6 +34,7 @@
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import java.util.Map;
import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -389,6 +390,132 @@
}
}
+ /**
+ * Returns the value of a particular memory statistic or {@code null} if no
+ * such memory statistic exists.
+ *
+ * <p>The following table lists the memory statistics that are supported.
+ * Note that memory statistics may be added or removed in a future API level.</p>
+ *
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Memory statistic name</th>
+ * <th>Meaning</th>
+ * <th>Example</th>
+ * <th>Supported (API Levels)</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>summary.java-heap</td>
+ * <td>The private Java Heap usage in kB. This corresponds to the Java Heap field
+ * in the App Summary section output by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.native-heap</td>
+ * <td>The private Native Heap usage in kB. This corresponds to the Native Heap
+ * field in the App Summary section output by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.code</td>
+ * <td>The memory usage for static code and resources in kB. This corresponds to
+ * the Code field in the App Summary section output by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.stack</td>
+ * <td>The stack usage in kB. This corresponds to the Stack field in the
+ * App Summary section output by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.graphics</td>
+ * <td>The graphics usage in kB. This corresponds to the Graphics field in the
+ * App Summary section output by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.private-other</td>
+ * <td>Other private memory usage in kB. This corresponds to the Private Other
+ * field output in the App Summary section by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.system</td>
+ * <td>Shared and system memory usage in kB. This corresponds to the System
+ * field output in the App Summary section by dumpsys meminfo.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.total-pss</td>
+ * <td>Total PPS memory usage in kB.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>summary.total-swap</td>
+ * <td>Total swap usage in kB.</td>
+ * <td>{@code 1442}</td>
+ * <td>23</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ */
+ public String getMemoryStat(String statName) {
+ switch(statName) {
+ case "summary.java-heap":
+ return Integer.toString(getSummaryJavaHeap());
+ case "summary.native-heap":
+ return Integer.toString(getSummaryNativeHeap());
+ case "summary.code":
+ return Integer.toString(getSummaryCode());
+ case "summary.stack":
+ return Integer.toString(getSummaryStack());
+ case "summary.graphics":
+ return Integer.toString(getSummaryGraphics());
+ case "summary.private-other":
+ return Integer.toString(getSummaryPrivateOther());
+ case "summary.system":
+ return Integer.toString(getSummarySystem());
+ case "summary.total-pss":
+ return Integer.toString(getSummaryTotalPss());
+ case "summary.total-swap":
+ return Integer.toString(getSummaryTotalSwap());
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns a map of the names/values of the memory statistics
+ * that {@link #getMemoryStat(String)} supports.
+ *
+ * @return a map of the names/values of the supported memory statistics.
+ */
+ public Map<String, String> getMemoryStats() {
+ Map<String, String> stats = new HashMap<String, String>();
+ stats.put("summary.java-heap", Integer.toString(getSummaryJavaHeap()));
+ stats.put("summary.native-heap", Integer.toString(getSummaryNativeHeap()));
+ stats.put("summary.code", Integer.toString(getSummaryCode()));
+ stats.put("summary.stack", Integer.toString(getSummaryStack()));
+ stats.put("summary.graphics", Integer.toString(getSummaryGraphics()));
+ stats.put("summary.private-other", Integer.toString(getSummaryPrivateOther()));
+ stats.put("summary.system", Integer.toString(getSummarySystem()));
+ stats.put("summary.total-pss", Integer.toString(getSummaryTotalPss()));
+ stats.put("summary.total-swap", Integer.toString(getSummaryTotalSwap()));
+ return stats;
+ }
+
/**
* Pss of Java Heap bytes in KB due to the application.
* Notes:
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 009649f..dbb5146 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -21,6 +21,7 @@
import android.system.Os;
import android.util.Log;
import com.android.internal.os.Zygote;
+import dalvik.system.VMRuntime;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
@@ -744,7 +745,14 @@
* @return Returns the number of milliseconds this process has return.
*/
public static final native long getElapsedCpuTime();
-
+
+ /**
+ * Returns true if the current process is a 64-bit runtime.
+ */
+ public static final boolean is64Bit() {
+ return VMRuntime.getRuntime().is64Bit();
+ }
+
/**
* Returns the identifier of this process, which can be used with
* {@link #killProcess} and {@link #sendSignal}.
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 4a4a375..5f83452 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -59,16 +59,16 @@
* <ol>
* <li>The carrier app package is updated, or</li>
* <li>The carrier app requests a reload with
- * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
- * reloadCarrierConfigForSubId}.</li>
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId}.</li>
* </ol>
* This method can be called after a SIM card loads, which may be before or after boot.
* </p>
* <p>
* This method should not block for a long time. If expensive operations (e.g. network access)
* are required, this method can schedule the work and return null. Then, use
- * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
- * reloadCarrierConfigForSubId} to trigger a reload when the config is ready.
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId} to trigger a reload when the config is ready.
* </p>
* <p>
* Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
diff --git a/core/java/android/util/LayoutDirection.java b/core/java/android/util/LayoutDirection.java
index 20af20b..03077e4 100644
--- a/core/java/android/util/LayoutDirection.java
+++ b/core/java/android/util/LayoutDirection.java
@@ -27,6 +27,12 @@
private LayoutDirection() {}
/**
+ * An undefined layout direction.
+ * @hide
+ */
+ public static final int UNDEFINED = -1;
+
+ /**
* Horizontal layout direction is from Left to Right.
*/
public static final int LTR = 0;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 342315b..5dd5ab8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1872,6 +1872,12 @@
public @interface ResolvedLayoutDir {}
/**
+ * A flag to indicate that the layout direction of this view has not been defined yet.
+ * @hide
+ */
+ public static final int LAYOUT_DIRECTION_UNDEFINED = LayoutDirection.UNDEFINED;
+
+ /**
* Horizontal layout direction of this view is from Left to Right.
* Use with {@link #setLayoutDirection}.
*/
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f153ce5..9d14254 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -185,6 +185,8 @@
private int mShowDividers;
private int mDividerPadding;
+ private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+
public LinearLayout(Context context) {
this(context, null);
}
@@ -1567,6 +1569,17 @@
}
}
+ @Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ if (layoutDirection != mLayoutDirection) {
+ mLayoutDirection = layoutDirection;
+ if (mOrientation == HORIZONTAL) {
+ requestLayout();
+ }
+ }
+ }
+
/**
* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #HORIZONTAL}.
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 644adb6..a2bd700 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -97,6 +97,11 @@
void showSessionForActiveService(IVoiceInteractionSessionShowCallback showCallback);
/**
+ * Hides the session from the active service, if it is showing.
+ */
+ void hideCurrentSession();
+
+ /**
* Notifies the active service that a launch was requested from the Keyguard. This will only
* be called if {@link #activeServiceSupportsLaunchFromKeyguard()} returns true.
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index e6165a1..4290e22 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -354,9 +354,9 @@
if (mBluetoothPowerCalculator == null) {
if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) {
- mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+ mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
} else {
- mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+ mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
}
}
mBluetoothPowerCalculator.reset();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 25228d0..087db78 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 126 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 127 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -7814,11 +7814,13 @@
mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
info.getControllerIdleTimeMillis());
- final double opVoltage = mPowerProfile.getAveragePower(
- PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
- if (opVoltage != 0) {
+ // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+ final double opVolt = mPowerProfile.getAveragePower(
+ PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+ if (opVolt != 0) {
+ // We store the power drain as mAms.
mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
- (long)(info.getControllerEnergyUsed() / opVoltage));
+ (long)(info.getControllerEnergyUsed() / opVolt));
}
}
}
@@ -7908,11 +7910,13 @@
mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
info.getControllerIdleTimeMillis());
- final double opVoltage = mPowerProfile.getAveragePower(
- PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
- if (opVoltage != 0) {
+ // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+ final double opVolt = mPowerProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+ if (opVolt != 0) {
+ // We store the power drain as mAms.
mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
- (long) (info.getControllerEnergyUsed() / opVoltage));
+ (long) (info.getControllerEnergyUsed() / opVolt));
}
}
}
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 3557209..1f59672 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -21,6 +21,15 @@
public class BluetoothPowerCalculator extends PowerCalculator {
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
private static final String TAG = "BluetoothPowerCalculator";
+ private final double mIdleMa;
+ private final double mRxMa;
+ private final double mTxMa;
+
+ public BluetoothPowerCalculator(PowerProfile profile) {
+ mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
+ mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
+ mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
+ }
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
@@ -37,10 +46,15 @@
BatteryStats.CONTROLLER_TX_TIME, statsType);
final long rxTimeMs = stats.getBluetoothControllerActivity(
BatteryStats.CONTROLLER_RX_TIME, statsType);
- final long powerMaMs = stats.getBluetoothControllerActivity(
- BatteryStats.CONTROLLER_POWER_DRAIN, statsType);
- final double powerMah = powerMaMs / (double)(1000*60*60);
final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
+ double powerMah = stats.getBluetoothControllerActivity(
+ BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);
+
+ if (powerMah == 0) {
+ // Some devices do not report the power, so calculate it.
+ powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
+ / (1000*60*60);
+ }
if (DEBUG && powerMah != 0) {
Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 961b0df..da98a67 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -70,14 +70,14 @@
statsType);
app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
- double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
+ double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
statsType) / (double)(1000*60*60);
- if (powerDrain == 0) {
+ if (powerDrainMah == 0) {
// Some controllers do not report power drain, so we can calculate it here.
- powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
+ powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
+ (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
}
- app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+ app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
if (DEBUG) {
Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah));
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c010dfe..294e4ba 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2741,7 +2741,14 @@
try {
mode = getCallback().onWindowStartingActionMode(wrappedCallback, type);
} catch (AbstractMethodError ame) {
- // Older apps might not implement this callback method.
+ // Older apps might not implement the typed version of this method.
+ if (type == ActionMode.TYPE_PRIMARY) {
+ try {
+ mode = getCallback().onWindowStartingActionMode(wrappedCallback);
+ } catch (AbstractMethodError ame2) {
+ // Older apps might not implement this callback method at all.
+ }
+ }
}
}
if (mode != null) {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3f828e7..baa7a35 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -414,8 +414,14 @@
<string name="silent_mode_ring">Ringer on</string>
<!-- Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery. -->
- <string name="reboot_to_recovery_title">Prepare for update</string>
- <string name="reboot_to_recovery_progress">Processing the update package\u2026</string>
+ <string name="reboot_to_update_title">Android system update</string>
+ <string name="reboot_to_update_prepare">Preparing to update\u2026</string>
+ <string name="reboot_to_update_package">Processing the update package\u2026</string>
+ <string name="reboot_to_update_reboot">Restarting\u2026</string>
+
+ <!-- Reboot to Recovery for factory reset. -->
+ <string name="reboot_to_reset_title">Factory data reset</string>
+ <string name="reboot_to_reset_message">Restarting\u2026</string>
<!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
<string name="shutdown_progress">Shutting down\u2026</string>
@@ -542,7 +548,7 @@
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_location">Location</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgroupdesc_location">access your location</string>
+ <string name="permgroupdesc_location">access this device\'s location</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_socialInfo">Your social information</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c7f27e1..c765563 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -820,8 +820,12 @@
<java-symbol type="string" name="mobile_provisioning_url" />
<java-symbol type="string" name="mobile_redirected_provisioning_url" />
<java-symbol type="string" name="quick_contacts_not_available" />
- <java-symbol type="string" name="reboot_to_recovery_progress" />
- <java-symbol type="string" name="reboot_to_recovery_title" />
+ <java-symbol type="string" name="reboot_to_update_package" />
+ <java-symbol type="string" name="reboot_to_update_prepare" />
+ <java-symbol type="string" name="reboot_to_update_title" />
+ <java-symbol type="string" name="reboot_to_update_reboot" />
+ <java-symbol type="string" name="reboot_to_reset_title" />
+ <java-symbol type="string" name="reboot_to_reset_message" />
<java-symbol type="string" name="reboot_safemode_confirm" />
<java-symbol type="string" name="reboot_safemode_title" />
<java-symbol type="string" name="relationTypeAssistant" />
diff --git a/core/tests/coretests/src/android/net/NetworkTest.java b/core/tests/coretests/src/android/net/NetworkTest.java
new file mode 100644
index 0000000..b0ecb049
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.net;
+
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.Network;
+import android.test.suitebuilder.annotation.SmallTest;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import junit.framework.TestCase;
+
+public class NetworkTest extends TestCase {
+ final Network mNetwork = new Network(99);
+
+ @SmallTest
+ public void testBindSocketOfInvalidFdThrows() throws Exception {
+
+ final FileDescriptor fd = new FileDescriptor();
+ assertFalse(fd.valid());
+
+ try {
+ mNetwork.bindSocket(fd);
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfNonSocketFdThrows() throws Exception {
+ final File devNull = new File("/dev/null");
+ assertTrue(devNull.canRead());
+
+ final FileInputStream fis = new FileInputStream(devNull);
+ assertTrue(null != fis.getFD());
+ assertTrue(fis.getFD().valid());
+
+ try {
+ mNetwork.bindSocket(fis.getFD());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception {
+ final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
+ mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53);
+ assertTrue(mDgramSocket.isConnected());
+
+ try {
+ mNetwork.bindSocket(mDgramSocket);
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfLocalSocketThrows() throws Exception {
+ final LocalSocket mLocalClient = new LocalSocket();
+ mLocalClient.bind(new LocalSocketAddress("testClient"));
+ assertTrue(mLocalClient.getFileDescriptor().valid());
+
+ try {
+ mNetwork.bindSocket(mLocalClient.getFileDescriptor());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+
+ final LocalServerSocket mLocalServer = new LocalServerSocket("testServer");
+ mLocalClient.connect(mLocalServer.getLocalSocketAddress());
+ assertTrue(mLocalClient.isConnected());
+
+ try {
+ mNetwork.bindSocket(mLocalClient.getFileDescriptor());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+}
diff --git a/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index 20963f5..fca958e 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -197,7 +197,7 @@
reset (e.g. by a Device Admin).</li>
<li>User authentication is required for every use of the key. In this mode, a specific operation
involving a specific key is authorized by the user. Currently, the only means of such
- authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, int) FingerprintManager.authenticate}.
+ authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler) FingerprintManager.authenticate}.
Such keys can only be generated or imported if at least one fingerprint is enrolled (see {@link android.hardware.fingerprint.FingerprintManager#hasEnrolledFingerprints() FingerprintManager.hasEnrolledFingerprints}).
These keys become permanently invalidated once all fingerprints are unenrolled.</li>
</ul>
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 817b7c9..059d8e6 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -29,11 +29,13 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.security.keystore.KeyInfo;
import android.security.keystore.KeyProperties;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.security.InvalidKeyException;
+import java.security.KeyFactory;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@@ -442,7 +444,20 @@
* imported or generated. This can be used to tell if there is special
* hardware support that can be used to bind keys to the device in a way
* that makes it non-exportable.
+ *
+ * @deprecated Whether the key is bound to the secure hardware is known only
+ * once the key has been imported. To find out, use:
+ * <pre>{@code
+ * PrivateKey key = ...; // private key from KeyChain
+ *
+ * KeyFactory keyFactory =
+ * KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
+ * KeyInfo keyInfo = keyFactory.getKeySpec(key, KeyInfo.class);
+ * if (keyInfo.isInsideSecureHardware()) {
+ * // The key is bound to the secure hardware of this Android
+ * }}</pre>
*/
+ @Deprecated
public static boolean isBoundKeyAlgorithm(
@NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
if (!isKeyAlgorithmSupported(algorithm)) {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 367257a..893771a 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -181,11 +181,15 @@
}
public boolean put(String key, byte[] value, int uid, int flags) {
+ return insert(key, value, uid, flags) == NO_ERROR;
+ }
+
+ public int insert(String key, byte[] value, int uid, int flags) {
try {
- return mBinder.insert(key, value, uid, flags) == NO_ERROR;
+ return mBinder.insert(key, value, uid, flags);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
- return false;
+ return SYSTEM_ERROR;
}
}
@@ -383,7 +387,7 @@
}
}
- // TODO remove this when it's removed from Settings
+ // TODO: remove this when it's removed from Settings
public boolean isHardwareBacked() {
return isHardwareBacked("RSA");
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index e555cc0..f37cf07 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -245,4 +245,12 @@
put("Signature." + algorithm + " SupportedKeyClasses",
KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
}
+
+ public static String[] getSupportedEcdsaSignatureDigests() {
+ return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+ }
+
+ public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() {
+ return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 0e8d03e..19375a2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -31,11 +31,18 @@
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
import java.security.Key;
+import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
import java.security.ProviderException;
+import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
@@ -43,7 +50,10 @@
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
/**
* Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
@@ -140,11 +150,18 @@
}
private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) {
- throw new UnsupportedOperationException(
- "Only ENCRYPT and DECRYPT modes supported. Mode: " + opmode);
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ case Cipher.WRAP_MODE:
+ mEncrypting = true;
+ break;
+ case Cipher.DECRYPT_MODE:
+ case Cipher.UNWRAP_MODE:
+ mEncrypting = false;
+ break;
+ default:
+ throw new InvalidParameterException("Unsupported opmode: " + opmode);
}
- mEncrypting = opmode == Cipher.ENCRYPT_MODE;
initKey(opmode, key);
if (mKey == null) {
throw new ProviderException("initKey did not initialize the key");
@@ -395,13 +412,139 @@
@Override
protected final byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
- return super.engineWrap(key);
+ if (mKey == null) {
+ throw new IllegalStateException("Not initilized");
+ }
+
+ if (!isEncrypting()) {
+ throw new IllegalStateException(
+ "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+ }
+
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ }
+ byte[] encoded = null;
+ if (key instanceof SecretKey) {
+ if ("RAW".equalsIgnoreCase(key.getFormat())) {
+ encoded = key.getEncoded();
+ }
+ if (encoded == null) {
+ try {
+ SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm());
+ SecretKeySpec spec =
+ (SecretKeySpec) keyFactory.getKeySpec(
+ (SecretKey) key, SecretKeySpec.class);
+ encoded = spec.getEncoded();
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new InvalidKeyException(
+ "Failed to wrap key because it does not export its key material",
+ e);
+ }
+ }
+ } else if (key instanceof PrivateKey) {
+ if ("PKCS8".equalsIgnoreCase(key.getFormat())) {
+ encoded = key.getEncoded();
+ }
+ if (encoded == null) {
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+ PKCS8EncodedKeySpec spec =
+ keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class);
+ encoded = spec.getEncoded();
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new InvalidKeyException(
+ "Failed to wrap key because it does not export its key material",
+ e);
+ }
+ }
+ } else if (key instanceof PublicKey) {
+ if ("X.509".equalsIgnoreCase(key.getFormat())) {
+ encoded = key.getEncoded();
+ }
+ if (encoded == null) {
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+ X509EncodedKeySpec spec =
+ keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
+ encoded = spec.getEncoded();
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new InvalidKeyException(
+ "Failed to wrap key because it does not export its key material",
+ e);
+ }
+ }
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName());
+ }
+
+ if (encoded == null) {
+ throw new InvalidKeyException(
+ "Failed to wrap key because it does not export its key material");
+ }
+
+ try {
+ return engineDoFinal(encoded, 0, encoded.length);
+ } catch (BadPaddingException e) {
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+ }
}
@Override
protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
- return super.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
+ if (mKey == null) {
+ throw new IllegalStateException("Not initilized");
+ }
+
+ if (isEncrypting()) {
+ throw new IllegalStateException(
+ "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+ }
+
+ if (wrappedKey == null) {
+ throw new NullPointerException("wrappedKey == null");
+ }
+
+ byte[] encoded;
+ try {
+ encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+ } catch (IllegalBlockSizeException | BadPaddingException e) {
+ throw new InvalidKeyException("Failed to unwrap key", e);
+ }
+
+ switch (wrappedKeyType) {
+ case Cipher.SECRET_KEY:
+ {
+ return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+ // break;
+ }
+ case Cipher.PRIVATE_KEY:
+ {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ try {
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(
+ "Failed to create private key from its PKCS#8 encoded form", e);
+ }
+ // break;
+ }
+ case Cipher.PUBLIC_KEY:
+ {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ try {
+ return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(
+ "Failed to create public key from its X.509 encoded form", e);
+ }
+ // break;
+ }
+ default:
+ throw new InvalidParameterException(
+ "Unsupported wrappedKeyType: " + wrappedKeyType);
+ }
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 4d6178f..688936c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -179,11 +179,15 @@
mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
+ if (spec.getSignaturePaddings().length > 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Signature paddings not supported for symmetric key algorithms");
+ }
mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : mKeymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
keymasterBlockMode)) {
throw new InvalidAlgorithmParameterException(
"Randomized encryption (IND-CPA) required but may be violated"
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index c5ea0f7..69155a8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -16,17 +16,39 @@
package android.security.keystore;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERInteger;
+import com.android.org.bouncycastle.asn1.DERNull;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.Certificate;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.org.bouncycastle.asn1.x509.Time;
+import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.org.bouncycastle.jce.X509Principal;
+import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
+import libcore.util.EmptyArray;
+
+import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -41,10 +63,19 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
/**
* Provides a way to create instances of a KeyPair which will be placed in the
@@ -63,13 +94,13 @@
public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
public RSA() {
- super(KeyProperties.KEY_ALGORITHM_RSA);
+ super(KeymasterDefs.KM_ALGORITHM_RSA);
}
}
public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
public EC() {
- super(KeyProperties.KEY_ALGORITHM_EC);
+ super(KeymasterDefs.KM_ALGORITHM_EC);
}
}
@@ -87,39 +118,296 @@
private static final int RSA_MIN_KEY_SIZE = 512;
private static final int RSA_MAX_KEY_SIZE = 8192;
- private final String mAlgorithm;
+ private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
+ new HashMap<String, Integer>();
+ private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
+ static {
+ // Aliases for NIST P-192
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-192", 192);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp192r1", 192);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime192v1", 192);
+
+ // Aliases for NIST P-224
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
+
+ // Aliases for NIST P-256
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+
+ // Aliases for NIST P-384
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
+
+ // Aliases for NIST P-521
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
+ SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
+
+ SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
+ Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
+ }
+ private final int mOriginalKeymasterAlgorithm;
private KeyStore mKeyStore;
private KeyGenParameterSpec mSpec;
+
+ private String mEntryAlias;
private boolean mEncryptionAtRestRequired;
- private @KeyProperties.KeyAlgorithmEnum String mKeyAlgorithm;
- private int mKeyType;
- private int mKeySize;
+ private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
+ private int mKeymasterAlgorithm = -1;
+ private int mKeySizeBits;
+ private SecureRandom mRng;
- protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
- mAlgorithm = algorithm;
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterEncryptionPaddings;
+ private int[] mKeymasterSignaturePaddings;
+ private int[] mKeymasterDigests;
+
+ private long mRSAPublicExponent;
+
+ protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
+ mOriginalKeymasterAlgorithm = keymasterAlgorithm;
}
- @KeyProperties.KeyAlgorithmEnum String getAlgorithm() {
- return mAlgorithm;
+ @Override
+ public void initialize(int keysize, SecureRandom random) {
+ throw new IllegalArgumentException(
+ KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName()
+ + " required to initialize this KeyPairGenerator");
}
- /**
- * Generate a KeyPair which is backed by the Android keystore service. You
- * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
- * with an {@link KeyPairGeneratorSpec} as the {@code params}
- * argument before calling this otherwise an {@code IllegalStateException}
- * will be thrown.
- * <p>
- * This will create an entry in the Android keystore service with a
- * self-signed certificate using the {@code params} specified in the
- * {@code initialize(params)} call.
- *
- * @throws IllegalStateException when called before calling
- * {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
- * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
- */
+ @Override
+ public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if (params == null) {
+ throw new InvalidAlgorithmParameterException(
+ "Must supply params of type " + KeyGenParameterSpec.class.getName()
+ + " or " + KeyPairGeneratorSpec.class.getName());
+ }
+
+ KeyGenParameterSpec spec;
+ boolean encryptionAtRestRequired = false;
+ int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
+ if (params instanceof KeyGenParameterSpec) {
+ spec = (KeyGenParameterSpec) params;
+ } else if (params instanceof KeyPairGeneratorSpec) {
+ // Legacy/deprecated spec
+ KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
+ try {
+ KeyGenParameterSpec.Builder specBuilder;
+ String specKeyAlgorithm = legacySpec.getKeyType();
+ if (specKeyAlgorithm != null) {
+ // Spec overrides the generator's default key algorithm
+ try {
+ keymasterAlgorithm =
+ KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+ specKeyAlgorithm);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(
+ "Invalid key type in parameters", e);
+ }
+ }
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ specBuilder = new KeyGenParameterSpec.Builder(
+ 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);
+ break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ specBuilder = new KeyGenParameterSpec.Builder(
+ legacySpec.getKeystoreAlias(),
+ KeyProperties.PURPOSE_ENCRYPT
+ | 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);
+ specBuilder.setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ specBuilder.setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_NONE,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ // Disable randomized encryption requirement to support encryption
+ // padding NONE above.
+ specBuilder.setRandomizedEncryptionRequired(false);
+ break;
+ default:
+ throw new ProviderException(
+ "Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+
+ if (legacySpec.getKeySize() != -1) {
+ specBuilder.setKeySize(legacySpec.getKeySize());
+ }
+ if (legacySpec.getAlgorithmParameterSpec() != null) {
+ specBuilder.setAlgorithmParameterSpec(
+ legacySpec.getAlgorithmParameterSpec());
+ }
+ specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
+ specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
+ specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
+ specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
+ encryptionAtRestRequired = legacySpec.isEncryptionRequired();
+ specBuilder.setUserAuthenticationRequired(false);
+
+ spec = specBuilder.build();
+ } catch (NullPointerException | IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+ } else {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported params class: " + params.getClass().getName()
+ + ". Supported: " + KeyGenParameterSpec.class.getName()
+ + ", " + KeyPairGeneratorSpec.class.getName());
+ }
+
+ mEntryAlias = spec.getKeystoreAlias();
+ mSpec = spec;
+ mKeymasterAlgorithm = keymasterAlgorithm;
+ mEncryptionAtRestRequired = encryptionAtRestRequired;
+ mKeySizeBits = spec.getKeySize();
+ initAlgorithmSpecificParameters();
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
+ }
+ checkValidKeySize(keymasterAlgorithm, mKeySizeBits);
+
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+
+ String jcaKeyAlgorithm;
+ try {
+ jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
+ keymasterAlgorithm);
+ mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+ mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+ mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster(
+ spec.getSignaturePaddings());
+ if (spec.isDigestsSpecified()) {
+ mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+ } else {
+ mKeymasterDigests = EmptyArray.INT;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+
+ mJcaKeyAlgorithm = jcaKeyAlgorithm;
+ mRng = random;
+ mKeyStore = KeyStore.getInstance();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void resetAll() {
+ mEntryAlias = null;
+ mJcaKeyAlgorithm = null;
+ mKeymasterAlgorithm = -1;
+ mKeymasterPurposes = null;
+ mKeymasterBlockModes = null;
+ mKeymasterEncryptionPaddings = null;
+ mKeymasterSignaturePaddings = null;
+ mKeymasterDigests = null;
+ mKeySizeBits = 0;
+ mSpec = null;
+ mRSAPublicExponent = -1;
+ mEncryptionAtRestRequired = false;
+ mRng = null;
+ mKeyStore = null;
+ }
+
+ private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
+ AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec();
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ {
+ BigInteger publicExponent = null;
+ if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
+ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = rsaSpec.getKeysize();
+ } else if (mKeySizeBits != rsaSpec.getKeysize()) {
+ throw new InvalidAlgorithmParameterException("RSA key size must match "
+ + " between " + mSpec + " and " + algSpecificSpec
+ + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize());
+ }
+ publicExponent = rsaSpec.getPublicExponent();
+ } else if (algSpecificSpec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "RSA may only use RSAKeyGenParameterSpec");
+ }
+ if (publicExponent == null) {
+ publicExponent = RSAKeyGenParameterSpec.F4;
+ }
+ if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
+ throw new InvalidAlgorithmParameterException(
+ "RSA public exponent must be positive: " + publicExponent);
+ }
+ if (publicExponent.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported RSA public exponent: " + publicExponent
+ + ". Only exponents <= " + Long.MAX_VALUE + " supported");
+ }
+ mRSAPublicExponent = publicExponent.longValue();
+ break;
+ }
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ if (algSpecificSpec instanceof ECGenParameterSpec) {
+ ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
+ String curveName = ecSpec.getName();
+ Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
+ curveName.toLowerCase(Locale.US));
+ if (ecSpecKeySizeBits == null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported EC curve name: " + curveName
+ + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
+ }
+ if (mKeySizeBits == -1) {
+ mKeySizeBits = ecSpecKeySizeBits;
+ } else if (mKeySizeBits != ecSpecKeySizeBits) {
+ throw new InvalidAlgorithmParameterException("EC key size must match "
+ + " between " + mSpec + " and " + algSpecificSpec
+ + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits);
+ }
+ } else if (algSpecificSpec != null) {
+ throw new InvalidAlgorithmParameterException(
+ "EC may only use ECGenParameterSpec");
+ }
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+ }
+
@Override
public KeyPair generateKeyPair() {
if (mKeyStore == null || mSpec == null) {
@@ -134,18 +422,65 @@
+ ", but the user has not yet entered the credential");
}
- final String alias = mSpec.getKeystoreAlias();
+ KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
+ args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
+ args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
- byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
+ // TODO: Remove the digest and padding NONE workaround below once Android Keystore returns
+ // keys which are backed by AndroidKeyStoreBCWorkaround provider instead of Conscrypt. The
+ // workaround is needed because Conscrypt (via keystore-engine) uses old KeyStore API which
+ // translates into digest NONE and padding NONE in the new API. keystore-engine cannot be
+ // updated to pass in the correct padding and digest values because it uses
+ // OpenSSL/BoringSSL engine which performs digesting and padding prior before invoking
+ // KeyStore API.
+ if (!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterDigests, KeymasterDefs.KM_DIGEST_NONE)) {
+ args.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+ }
+ if ((!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterSignaturePaddings, KeymasterDefs.KM_PAD_NONE))
+ && (!com.android.internal.util.ArrayUtils.contains(
+ mKeymasterEncryptionPaddings, KeymasterDefs.KM_PAD_NONE))) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+ }
- final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+ KeymasterUtils.addUserAuthArgs(args,
+ mSpec.isUserAuthenticationRequired(),
+ mSpec.getUserAuthenticationValidityDurationSeconds());
+ args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+ (mSpec.getKeyValidityStart() != null)
+ ? mSpec.getKeyValidityStart() : new Date(0));
+ args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ (mSpec.getKeyValidityForOriginationEnd() != null)
+ ? mSpec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+ args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ (mSpec.getKeyValidityForConsumptionEnd() != null)
+ ? mSpec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+ addAlgorithmSpecificParameters(args);
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, (mKeySizeBits + 7) / 8);
+
+ final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
boolean success = false;
try {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
- flags, args)) {
- throw new IllegalStateException("could not generate key in keystore");
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+ int errorCode = mKeyStore.generateKey(
+ privateKeyAlias,
+ args,
+ additionalEntropy,
+ flags,
+ resultingKeyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new ProviderException(
+ "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
}
final PrivateKey privKey;
@@ -153,7 +488,7 @@
try {
privKey = engine.getPrivateKeyById(privateKeyAlias);
} catch (InvalidKeyException e) {
- throw new RuntimeException("Can't get key", e);
+ throw new ProviderException("Failed to obtain generated private key", e);
}
ExportResult exportResult =
@@ -163,39 +498,45 @@
throw new KeyStoreConnectException();
} else if (exportResult.resultCode != KeyStore.NO_ERROR) {
throw new ProviderException(
- "Failed to obtain public key in X.509 format",
+ "Failed to obtain X.509 form of generated public key",
KeyStore.getKeyStoreException(exportResult.resultCode));
}
final byte[] pubKeyBytes = exportResult.exportData;
-
final PublicKey pubKey;
try {
- final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
+ final KeyFactory keyFact = KeyFactory.getInstance(mJcaKeyAlgorithm);
pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
} catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("Can't instantiate key generator", e);
+ throw new ProviderException(
+ "Failed to obtain " + mJcaKeyAlgorithm + " KeyFactory", e);
} catch (InvalidKeySpecException e) {
- throw new IllegalStateException("keystore returned invalid key encoding", e);
+ throw new ProviderException("Invalid X.509 encoding of generated public key", e);
}
final X509Certificate cert;
try {
- cert = generateCertificate(privKey, pubKey);
+ cert = generateSelfSignedCertificate(privKey, pubKey);
} catch (Exception e) {
- throw new IllegalStateException("Can't generate certificate", e);
+ throw new ProviderException("Failed to generate self-signed certificate", e);
}
byte[] certBytes;
try {
certBytes = cert.getEncoded();
} catch (CertificateEncodingException e) {
- throw new IllegalStateException("Can't get encoding of certificate", e);
+ throw new ProviderException(
+ "Failed to obtain encoded form of self-signed certificate", e);
}
- if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
- flags)) {
- throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+ int insertErrorCode = mKeyStore.insert(
+ Credentials.USER_CERTIFICATE + mEntryAlias,
+ certBytes,
+ KeyStore.UID_SELF,
+ flags);
+ if (insertErrorCode != KeyStore.NO_ERROR) {
+ throw new ProviderException("Failed to store self-signed certificate",
+ KeyStore.getKeyStoreException(insertErrorCode));
}
KeyPair result = new KeyPair(pubKey, privKey);
@@ -203,14 +544,41 @@
return result;
} finally {
if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias);
}
}
}
+ private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ keymasterArgs.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
+ break;
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+ }
+
+ private X509Certificate generateSelfSignedCertificate(
+ PrivateKey privateKey, PublicKey publicKey) throws Exception {
+ String signatureAlgorithm =
+ getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
+ if (signatureAlgorithm == null) {
+ // Key cannot be used to sign a certificate
+ return generateSelfSignedCertificateWithFakeSignature(publicKey);
+ } else {
+ // Key can be used to sign a certificate
+ return generateSelfSignedCertificateWithValidSignature(
+ privateKey, publicKey, signatureAlgorithm);
+ }
+ }
+
@SuppressWarnings("deprecation")
- private X509Certificate generateCertificate(PrivateKey privateKey, PublicKey publicKey)
- throws Exception {
+ private X509Certificate generateSelfSignedCertificateWithValidSignature(
+ PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm)
+ throws Exception {
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setPublicKey(publicKey);
certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
@@ -218,198 +586,223 @@
certGen.setIssuerDN(mSpec.getCertificateSubject());
certGen.setNotBefore(mSpec.getCertificateNotBefore());
certGen.setNotAfter(mSpec.getCertificateNotAfter());
- certGen.setSignatureAlgorithm(getDefaultSignatureAlgorithmForKeyAlgorithm(mKeyAlgorithm));
+ certGen.setSignatureAlgorithm(signatureAlgorithm);
return certGen.generate(privateKey);
}
- @NonNull
- private @KeyProperties.KeyAlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
- String result = spec.getKeyType();
- if (result != null) {
- return result;
+ @SuppressWarnings("deprecation")
+ private X509Certificate generateSelfSignedCertificateWithFakeSignature(
+ PublicKey publicKey) throws Exception {
+ V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
+ ASN1ObjectIdentifier sigAlgOid;
+ AlgorithmIdentifier sigAlgId;
+ byte[] signature;
+ switch (mKeymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
+ sigAlgId = new AlgorithmIdentifier(sigAlgOid);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERInteger(0));
+ v.add(new DERInteger(0));
+ signature = new DERSequence().getEncoded();
+ break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption;
+ sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE);
+ signature = new byte[1];
+ break;
+ default:
+ throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm);
}
- return getAlgorithm();
+
+ try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) {
+ tbsGenerator.setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
+ }
+ tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber()));
+ X509Principal subject =
+ new X509Principal(mSpec.getCertificateSubject().getEncoded());
+ tbsGenerator.setSubject(subject);
+ tbsGenerator.setIssuer(subject);
+ tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore()));
+ tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter()));
+ tbsGenerator.setSignature(sigAlgId);
+ TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate();
+
+ ASN1EncodableVector result = new ASN1EncodableVector();
+ result.add(tbsCertificate);
+ result.add(sigAlgId);
+ result.add(new DERBitString(signature));
+ return new X509CertificateObject(Certificate.getInstance(new DERSequence(result)));
}
- private static int getDefaultKeySize(int keyType) {
- if (keyType == NativeConstants.EVP_PKEY_EC) {
- return EC_DEFAULT_KEY_SIZE;
- } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
- return RSA_DEFAULT_KEY_SIZE;
+ private static int getDefaultKeySize(int keymasterAlgorithm) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ return EC_DEFAULT_KEY_SIZE;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ return RSA_DEFAULT_KEY_SIZE;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
}
- return -1;
}
- private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
+ private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
throws InvalidAlgorithmParameterException {
- if (keyType == NativeConstants.EVP_PKEY_EC) {
- if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
- throw new InvalidAlgorithmParameterException("EC keys must be >= "
- + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
- }
- } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
- if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
- throw new InvalidAlgorithmParameterException("RSA keys must be >= "
- + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
- }
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- }
-
- private static void checkCorrectParametersSpec(int keyType, int keySize,
- AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
- if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) {
- if (spec instanceof RSAKeyGenParameterSpec) {
- RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
- if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
- throw new InvalidAlgorithmParameterException("RSA key size must match: "
- + keySize + " vs " + rsaSpec.getKeysize());
- }
- } else {
- throw new InvalidAlgorithmParameterException(
- "RSA may only use RSAKeyGenParameterSpec");
- }
- }
- }
-
- private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
- @KeyProperties.KeyAlgorithmEnum String algorithm) {
- if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
- return "sha256WithRSA";
- } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
- return "sha256WithECDSA";
- } else {
- throw new IllegalArgumentException("Unsupported key type " + algorithm);
- }
- }
-
- private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
- switch (keyType) {
- case NativeConstants.EVP_PKEY_RSA:
- if (spec instanceof RSAKeyGenParameterSpec) {
- RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
- return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+ throw new InvalidAlgorithmParameterException("EC key size must be >= "
+ + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
}
break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+ throw new InvalidAlgorithmParameterException("RSA key size must be >= "
+ + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
+ }
+ break;
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
}
- return null;
}
- @Override
- public void initialize(int keysize, SecureRandom random) {
- throw new IllegalArgumentException(
- "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
- }
+ /**
+ * Returns the {@code Signature} algorithm to be used for signing a certificate using the
+ * specified key or {@code null} if the key cannot be used for signing a certificate.
+ */
+ @Nullable
+ private static String getCertificateSignatureAlgorithm(
+ int keymasterAlgorithm,
+ int keySizeBits,
+ KeyGenParameterSpec spec) {
+ // Constraints:
+ // 1. Key must be authorized for signing.
+ // 2. Signature digest must be one of key's authorized digests.
+ // 3. For RSA keys, the digest output size must not exceed modulus size minus space needed
+ // for RSA PKCS#1 signature padding (about 29 bytes: minimum 10 bytes of padding + 15--19
+ // bytes overhead for encoding digest OID and digest value in DER).
+ // 4. For EC keys, the there is no point in using a digest whose output size is longer than
+ // key/field size because the digest will be truncated to that size.
- @Override
- public void initialize(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- if (params == null) {
- throw new InvalidAlgorithmParameterException(
- "Must supply params of type " + KeyGenParameterSpec.class.getName()
- + " or " + KeyPairGeneratorSpec.class.getName());
+ if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
+ // Key not authorized for signing
+ return null;
}
+ if (!spec.isDigestsSpecified()) {
+ // Key not authorized for any digests -- can't sign
+ return null;
+ }
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ {
+ Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+ spec.getDigests(),
+ AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
- String keyAlgorithm;
- KeyGenParameterSpec spec;
- boolean encryptionAtRestRequired = false;
- if (params instanceof KeyPairGeneratorSpec) {
- KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
- try {
- KeyGenParameterSpec.Builder specBuilder;
- keyAlgorithm = getKeyAlgorithm(legacySpec).toUpperCase(Locale.US);
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder = new KeyGenParameterSpec.Builder(
- 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);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_ENCRYPT
- | 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);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
- specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
- // Disable randomized encryption requirement to support encryption padding NONE
- // above.
- specBuilder.setRandomizedEncryptionRequired(false);
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
+ int bestKeymasterDigest = -1;
+ int bestDigestOutputSizeBits = -1;
+ for (int keymasterDigest : availableKeymasterDigests) {
+ int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+ if (outputSizeBits == keySizeBits) {
+ // Perfect match -- use this digest
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ break;
+ }
+ // Not a perfect match -- check against the best digest so far
+ if (bestKeymasterDigest == -1) {
+ // First digest tested -- definitely the best so far
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ } else {
+ // Prefer output size to be as close to key size as possible, with output
+ // sizes larger than key size preferred to those smaller than key size.
+ if (bestDigestOutputSizeBits < keySizeBits) {
+ // Output size of the best digest so far is smaller than key size.
+ // Anything larger is a win.
+ if (outputSizeBits > bestDigestOutputSizeBits) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ } else {
+ // Output size of the best digest so far is larger than key size.
+ // Anything smaller is a win, as long as it's not smaller than key size.
+ if ((outputSizeBits < bestDigestOutputSizeBits)
+ && (outputSizeBits >= keySizeBits)) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ }
+ }
}
-
- if (legacySpec.getKeySize() != -1) {
- specBuilder.setKeySize(legacySpec.getKeySize());
+ if (bestKeymasterDigest == -1) {
+ return null;
}
- if (legacySpec.getAlgorithmParameterSpec() != null) {
- specBuilder.setAlgorithmParameterSpec(legacySpec.getAlgorithmParameterSpec());
- }
- specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
- specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
- specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
- specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
- encryptionAtRestRequired = legacySpec.isEncryptionRequired();
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(e);
+ return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+ bestKeymasterDigest) + "WithECDSA";
}
- } else if (params instanceof KeyGenParameterSpec) {
- spec = (KeyGenParameterSpec) params;
- keyAlgorithm = getAlgorithm();
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ {
+ Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+ spec.getDigests(),
+ AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
+
+ // The amount of space available for the digest is less than modulus size because
+ // padding must be at least 10 bytes long, and then there's also the 15--19
+ // bytes overhead for encoding digest OID and digest value in DER.
+ int maxDigestOutputSizeBits = keySizeBits - 29 * 8;
+ int bestKeymasterDigest = -1;
+ int bestDigestOutputSizeBits = -1;
+ for (int keymasterDigest : availableKeymasterDigests) {
+ int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+ if (outputSizeBits > maxDigestOutputSizeBits) {
+ // Digest too long (signature generation will fail) -- skip
+ continue;
+ }
+ if (bestKeymasterDigest == -1) {
+ // First digest tested -- definitely the best so far
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ } else {
+ // The longer the better
+ if (outputSizeBits > bestDigestOutputSizeBits) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ }
+ }
+ if (bestKeymasterDigest == -1) {
+ return null;
+ }
+ return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+ bestKeymasterDigest) + "WithRSA";
+ }
+ default:
+ throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+ }
+ }
+
+ private static Set<Integer> getAvailableKeymasterSignatureDigests(
+ @KeyProperties.DigestEnum String[] authorizedKeyDigests,
+ @KeyProperties.DigestEnum String[] supportedSignatureDigests) {
+ Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>();
+ for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) {
+ authorizedKeymasterKeyDigests.add(keymasterDigest);
+ }
+ Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>();
+ for (int keymasterDigest
+ : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) {
+ supportedKeymasterSignatureDigests.add(keymasterDigest);
+ }
+ if (authorizedKeymasterKeyDigests.contains(KeymasterDefs.KM_DIGEST_NONE)) {
+ // Key is authorized to be used with any digest
+ return supportedKeymasterSignatureDigests;
} else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported params class: " + params.getClass().getName()
- + ". Supported: " + KeyGenParameterSpec.class.getName()
- + ", " + KeyPairGeneratorSpec.class);
+ // Key is authorized to be used only with specific digests.
+ Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
+ result.retainAll(authorizedKeymasterKeyDigests);
+ return result;
}
-
- int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
- if (keyType == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- int keySize = spec.getKeySize();
- if (keySize == -1) {
- keySize = getDefaultKeySize(keyType);
- if (keySize == -1) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key algorithm: " + keyAlgorithm);
- }
- }
- checkCorrectParametersSpec(keyType, keySize, spec.getAlgorithmParameterSpec());
- checkValidKeySize(keyAlgorithm, keyType, keySize);
-
- mKeyAlgorithm = keyAlgorithm;
- mKeyType = keyType;
- mKeySize = keySize;
- mSpec = spec;
- mEncryptionAtRestRequired = encryptionAtRestRequired;
- mKeyStore = KeyStore.getInstance();
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index 5643caf..d33692a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -453,7 +453,7 @@
case Cipher.ENCRYPT_MODE:
case Cipher.WRAP_MODE:
// Permitted
- return;
+ break;
case Cipher.DECRYPT_MODE:
case Cipher.UNWRAP_MODE:
throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 7c9c0cf..c03be63 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -22,6 +22,7 @@
import libcore.util.EmptyArray;
import android.security.Credentials;
+import android.security.KeyStore;
import android.security.KeyStoreParameter;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
@@ -39,7 +40,6 @@
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
-import java.security.KeyStore;
import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
@@ -86,7 +86,7 @@
public class AndroidKeyStoreSpi extends KeyStoreSpi {
public static final String NAME = "AndroidKeyStore";
- private android.security.KeyStore mKeyStore;
+ private KeyStore mKeyStore;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
@@ -105,8 +105,7 @@
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
int errorCode = mKeyStore.getKeyCharacteristics(
keyAliasInKeystore, null, null, keyCharacteristics);
- if ((errorCode != KeymasterDefs.KM_ERROR_OK)
- && (errorCode != android.security.KeyStore.NO_ERROR)) {
+ if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Failed to load information about key")
.initCause(mKeyStore.getInvalidKeyException(alias, errorCode));
@@ -272,107 +271,72 @@
}
}
+ private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
+ throws KeyStoreException {
+ String keyAlgorithm = key.getAlgorithm();
+ KeyProtection.Builder specBuilder;
+ if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
+ 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);
+ } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
+ specBuilder =
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT
+ | 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);
+ specBuilder.setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ specBuilder.setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_NONE,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ // Disable randomized encryption requirement to support encryption padding NONE
+ // above.
+ specBuilder.setRandomizedEncryptionRequired(false);
+ } else {
+ throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
+ }
+ specBuilder.setUserAuthenticationRequired(false);
+
+ return specBuilder.build();
+ }
+
private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
int flags = 0;
KeyProtection spec;
- if (param instanceof KeyStoreParameter) {
+ if (param == null) {
+ spec = getLegacyKeyProtectionParameter(key);
+ } else if (param instanceof KeyStoreParameter) {
+ spec = getLegacyKeyProtectionParameter(key);
KeyStoreParameter legacySpec = (KeyStoreParameter) param;
- try {
- String keyAlgorithm = key.getAlgorithm();
- KeyProtection.Builder specBuilder;
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- 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);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder =
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_ENCRYPT
- | 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);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
- specBuilder.setBlockModes(KeyProperties.BLOCK_MODE_ECB);
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
- // Disable randomized encryption requirement to support encryption padding NONE
- // above.
- specBuilder.setRandomizedEncryptionRequired(false);
- } else {
- throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
- }
- if (legacySpec.isEncryptionRequired()) {
- flags = android.security.KeyStore.FLAG_ENCRYPTED;
- }
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new KeyStoreException("Unsupported protection parameter", e);
+ if (legacySpec.isEncryptionRequired()) {
+ flags = KeyStore.FLAG_ENCRYPTED;
}
} else if (param instanceof KeyProtection) {
spec = (KeyProtection) param;
- } else if (param != null) {
+ } else {
throw new KeyStoreException(
"Unsupported protection parameter class:" + param.getClass().getName()
- + ". Supported: " + KeyStoreParameter.class.getName() + ", "
- + KeyProtection.class.getName());
- } else {
- spec = null;
- }
-
- byte[] keyBytes = null;
-
- final String pkeyAlias;
- if (key instanceof OpenSSLKeyHolder) {
- pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
- } else {
- pkeyAlias = null;
- }
-
- final boolean shouldReplacePrivateKey;
- if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
- final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
- if (!alias.equals(keySubalias)) {
- throw new KeyStoreException("Can only replace keys with same alias: " + alias
- + " != " + keySubalias);
- }
-
- shouldReplacePrivateKey = false;
- } else {
- // Make sure the PrivateKey format is the one we support.
- final String keyFormat = key.getFormat();
- if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
- throw new KeyStoreException(
- "Only PrivateKeys that can be encoded into PKCS#8 are supported");
- }
-
- // Make sure we can actually encode the key.
- keyBytes = key.getEncoded();
- if (keyBytes == null) {
- throw new KeyStoreException("PrivateKey has no encoding");
- }
-
- shouldReplacePrivateKey = true;
+ + ". Supported: " + KeyProtection.class.getName() + ", "
+ + KeyStoreParameter.class.getName());
}
// Make sure the chain exists since this is a PrivateKey
@@ -400,7 +364,7 @@
try {
userCertBytes = x509chain[0].getEncoded();
} catch (CertificateEncodingException e) {
- throw new KeyStoreException("Couldn't encode certificate #1", e);
+ throw new KeyStoreException("Failed to encode certificate #0", e);
}
/*
@@ -421,7 +385,7 @@
certsBytes[i] = x509chain[i + 1].getEncoded();
totalCertLength += certsBytes[i].length;
} catch (CertificateEncodingException e) {
- throw new KeyStoreException("Can't encode Certificate #" + i, e);
+ throw new KeyStoreException("Failed to encode certificate #" + i, e);
}
}
@@ -441,31 +405,150 @@
chainBytes = null;
}
- /*
- * Make sure we clear out all the appropriate types before trying to
- * write.
- */
- if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ final String pkeyAlias;
+ if (key instanceof OpenSSLKeyHolder) {
+ pkeyAlias = ((OpenSSLKeyHolder) key).getOpenSSLKey().getAlias();
+ } else if (key instanceof AndroidKeyStorePrivateKey) {
+ pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
} else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ pkeyAlias = null;
}
- if (shouldReplacePrivateKey
- && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put private key in keystore");
- } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put certificate #1 in keystore");
- } else if (chainBytes != null
- && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
- android.security.KeyStore.UID_SELF, flags)) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- throw new KeyStoreException("Couldn't put certificate chain in keystore");
+ byte[] pkcs8EncodedPrivateKeyBytes;
+ KeymasterArguments importArgs;
+ final boolean shouldReplacePrivateKey;
+ if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
+ final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
+ if (!alias.equals(keySubalias)) {
+ throw new KeyStoreException("Can only replace keys with same alias: " + alias
+ + " != " + keySubalias);
+ }
+ shouldReplacePrivateKey = false;
+ importArgs = null;
+ pkcs8EncodedPrivateKeyBytes = null;
+ } else {
+ shouldReplacePrivateKey = true;
+ // Make sure the PrivateKey format is the one we support.
+ final String keyFormat = key.getFormat();
+ if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+ throw new KeyStoreException(
+ "Unsupported private key export format: " + keyFormat
+ + ". Only private keys which export their key material in PKCS#8 format are"
+ + " supported.");
+ }
+
+ // Make sure we can actually encode the key.
+ pkcs8EncodedPrivateKeyBytes = key.getEncoded();
+ if (pkcs8EncodedPrivateKeyBytes == null) {
+ throw new KeyStoreException("Private key did not export any key material");
+ }
+
+ importArgs = new KeymasterArguments();
+ try {
+ importArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+ KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+ key.getAlgorithm()));
+ @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
+ importArgs.addInts(KeymasterDefs.KM_TAG_PURPOSE,
+ KeyProperties.Purpose.allToKeymaster(purposes));
+ if (spec.isDigestsSpecified()) {
+ importArgs.addInts(KeymasterDefs.KM_TAG_DIGEST,
+ KeyProperties.Digest.allToKeymaster(spec.getDigests()));
+ }
+
+ importArgs.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
+ int[] keymasterEncryptionPaddings =
+ KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
+ && (spec.isRandomizedEncryptionRequired())) {
+ for (int keymasterPadding : keymasterEncryptionPaddings) {
+ if (!KeymasterUtils
+ .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ keymasterPadding)) {
+ throw new KeyStoreException(
+ "Randomized encryption (IND-CPA) required but is violated by"
+ + " encryption padding mode: "
+ + KeyProperties.EncryptionPadding.fromKeymaster(
+ keymasterPadding)
+ + ". See KeyProtection documentation.");
+ }
+ }
+ }
+ importArgs.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
+ importArgs.addInts(KeymasterDefs.KM_TAG_PADDING,
+ KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
+ KeymasterUtils.addUserAuthArgs(importArgs,
+ spec.isUserAuthenticationRequired(),
+ spec.getUserAuthenticationValidityDurationSeconds());
+ importArgs.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+ (spec.getKeyValidityStart() != null)
+ ? spec.getKeyValidityStart() : new Date(0));
+ importArgs.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ (spec.getKeyValidityForOriginationEnd() != null)
+ ? spec.getKeyValidityForOriginationEnd()
+ : new Date(Long.MAX_VALUE));
+ importArgs.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ (spec.getKeyValidityForConsumptionEnd() != null)
+ ? spec.getKeyValidityForConsumptionEnd()
+ : new Date(Long.MAX_VALUE));
+ } catch (IllegalArgumentException e) {
+ throw new KeyStoreException("Invalid parameter", e);
+ }
+ }
+
+
+ boolean success = false;
+ try {
+ // Store the private key, if necessary
+ if (shouldReplacePrivateKey) {
+ // Delete the stored private key and any related entries before importing the
+ // provided key
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
+ int errorCode = mKeyStore.importKey(
+ Credentials.USER_PRIVATE_KEY + alias,
+ importArgs,
+ KeymasterDefs.KM_KEY_FORMAT_PKCS8,
+ pkcs8EncodedPrivateKeyBytes,
+ flags,
+ resultingKeyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store private key",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+ } else {
+ // Keep the stored private key around -- delete all other entry types
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ }
+
+ // Store the leaf certificate
+ int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
+ KeyStore.UID_SELF, flags);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store certificate #0",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+
+ // Store the certificate chain
+ errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
+ KeyStore.UID_SELF, flags);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw new KeyStoreException("Failed to store certificate chain",
+ KeyStore.getKeyStoreException(errorCode));
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ if (shouldReplacePrivateKey) {
+ Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+ } else {
+ Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+ Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+ }
+ }
}
}
@@ -589,7 +672,8 @@
if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ keymasterBlockMode)) {
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
@@ -598,9 +682,7 @@
}
}
}
- for (int keymasterPurpose : KeyProperties.Purpose.allToKeymaster(purposes)) {
- args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
- }
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
if (params.getSignaturePaddings().length > 0) {
throw new KeyStoreException("Signature paddings not supported for symmetric keys");
@@ -636,7 +718,7 @@
keyMaterial,
0, // flags
new KeyCharacteristics());
- if (errorCode != android.security.KeyStore.NO_ERROR) {
+ if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to import secret key. Keystore error code: "
+ errorCode);
}
@@ -667,7 +749,7 @@
}
if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
- android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
@@ -840,7 +922,7 @@
}
// Unfortunate name collision.
- mKeyStore = android.security.KeyStore.getInstance();
+ mKeyStore = KeyStore.getInstance();
}
@Override
@@ -852,8 +934,9 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
- if (entry instanceof KeyStore.TrustedCertificateEntry) {
- KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
+ if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
+ java.security.KeyStore.TrustedCertificateEntry trE =
+ (java.security.KeyStore.TrustedCertificateEntry) entry;
engineSetCertificateEntry(alias, trE.getTrustedCertificate());
return;
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d861302..19ff9c7 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -214,7 +214,9 @@
}
/**
- * Returns the requested key size or {@code -1} if default size should be used.
+ * Returns the requested key size. If {@code -1}, the size should be looked up from
+ * {@link #getAlgorithmParameterSpec()}, if provided, otherwise an algorithm-specific default
+ * size should be used.
*/
public int getKeySize() {
return mKeySize;
@@ -465,7 +467,10 @@
* the modulus size, for EC keys this selects a curve with a matching field size, and for
* symmetric keys this sets the size of the bitstring which is their key material.
*
- * <p>The default key size is specific to each key algorithm.
+ * <p>The default key size is specific to each key algorithm. If key size is not set
+ * via this method, it should be looked up from the algorithm-specific parameters (if any)
+ * provided via
+ * {@link #setAlgorithmParameterSpec(AlgorithmParameterSpec) setAlgorithmParameterSpec}.
*/
@NonNull
public Builder setKeySize(int keySize) {
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index e3c2d1d..5af4181 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -168,6 +168,31 @@
public static abstract class KeyAlgorithm {
private KeyAlgorithm() {}
+ public static int toKeymasterAsymmetricKeyAlgorithm(
+ @NonNull @KeyAlgorithmEnum String algorithm) {
+ if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_EC;
+ } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_RSA;
+ } else {
+ throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+ }
+ }
+
+ @NonNull
+ public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
+ int keymasterAlgorithm) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ return KEY_ALGORITHM_EC;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ return KEY_ALGORITHM_RSA;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported key algorithm: " + keymasterAlgorithm);
+ }
+ }
+
public static int toKeymasterSecretKeyAlgorithm(
@NonNull @KeyAlgorithmEnum String algorithm) {
if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
@@ -572,6 +597,28 @@
}
@NonNull
+ public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return "NONE";
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return "MD5";
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return "SHA1";
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return "SHA224";
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return "SHA256";
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return "SHA384";
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return "SHA512";
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ @NonNull
public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
if (digests.isEmpty()) {
return EmptyArray.STRING;
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index e7529e1..0639d49 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -50,7 +50,8 @@
}
}
- public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
+ public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ int keymasterBlockMode) {
switch (keymasterBlockMode) {
case KeymasterDefs.KM_MODE_ECB:
return false;
@@ -63,6 +64,20 @@
}
}
+ public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ int keymasterPadding) {
+ switch (keymasterPadding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return false;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return true;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + keymasterPadding);
+ }
+ }
+
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index e59688c..5147e98 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -215,16 +215,6 @@
return texture;
}
-Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
- Texture* texture = new Texture(Caches::getInstance());
- texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
- texture->cleanup = true;
-
- generateTexture(bitmap, texture, false);
-
- return texture;
-}
-
void TextureCache::releaseTexture(uint32_t pixelRefStableID) {
Mutex::Autolock _l(mLock);
mGarbage.push(pixelRefStableID);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index a2c6380..e7fc990 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -80,11 +80,6 @@
* cannot be found in the cache, a new texture is generated.
*/
Texture* get(const SkBitmap* bitmap);
- /**
- * Returns the texture associated with the specified bitmap. The generated
- * texture is not kept in the cache. The caller must destroy the texture.
- */
- Texture* getTransient(const SkBitmap* bitmap);
/**
* Removes the texture associated with the specified pixelRef. This is meant
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7eb13573..3cbc405 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1277,7 +1277,8 @@
native_enableDeviceCallback();
}
mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7293c6c..f395cb3 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2244,7 +2244,8 @@
native_enableDeviceCallback();
}
mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index aa5d43a..6bf5721 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -57,6 +57,16 @@
public static final String TAG_APERTURE = "FNumber";
/** Type is String. */
public static final String TAG_ISO = "ISOSpeedRatings";
+ /** Type is String. */
+ public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME = "SubSecTime";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+
+
/**
* @hide
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 4b1fa13..c537dd6 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -549,8 +549,9 @@
private static final String generateInputIdForHdmiDevice(
ComponentName name, HdmiDeviceInfo deviceInfo) {
// Example of the format : "/HDMI%04X%02X"
- String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE,
- LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_DEVICE_ID);
+ String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE
+ + "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X"
+ + "%0" + LENGTH_HDMI_DEVICE_ID + "X";
return name.flattenToShortString() + String.format(format,
deviceInfo.getPhysicalAddress(), deviceInfo.getId());
}
@@ -564,8 +565,8 @@
*/
private static final String generateInputIdForHardware(
ComponentName name, TvInputHardwareInfo hardwareInfo) {
- return name.flattenToShortString() + String.format("%s%s%d",
- DELIMITER_INFO_IN_ID, PREFIX_HARDWARE_DEVICE, hardwareInfo.getDeviceId());
+ return name.flattenToShortString() + DELIMITER_INFO_IN_ID + PREFIX_HARDWARE_DEVICE
+ + hardwareInfo.getDeviceId();
}
public static final Parcelable.Creator<TvInputInfo> CREATOR =
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 96b72a2..fdc586b 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -92,6 +92,7 @@
}
}
+static Mutex sLock;
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
@@ -182,6 +183,32 @@
}
// ----------------------------------------------------------------------------
+
+static sp<AudioEffect> getAudioEffect(JNIEnv* env, jobject thiz)
+{
+ Mutex::Autolock l(sLock);
+ AudioEffect* const ae =
+ (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+ return sp<AudioEffect>(ae);
+}
+
+static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
+ const sp<AudioEffect>& ae)
+{
+ Mutex::Autolock l(sLock);
+ sp<AudioEffect> old =
+ (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+ if (ae.get()) {
+ ae->incStrong((void*)setAudioEffect);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setAudioEffect);
+ }
+ env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)ae.get());
+ return old;
+}
+
+// ----------------------------------------------------------------------------
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in AudioEffect, which won't run until the
// first time an instance of this class is used.
@@ -257,7 +284,7 @@
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
- AudioEffect* lpAudioEffect = NULL;
+ sp<AudioEffect> lpAudioEffect;
jint* nId = NULL;
const char *typeStr = NULL;
const char *uuidStr = NULL;
@@ -272,6 +299,8 @@
ScopedUtfChars opPackageNameStr(env, opPackageName);
+ setAudioEffect(env, thiz, 0);
+
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
@@ -324,7 +353,7 @@
&lpJniStorage->mCallbackData,
sessionId,
0);
- if (lpAudioEffect == NULL) {
+ if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
}
@@ -394,7 +423,7 @@
env->SetObjectArrayElement(javadesc, 0, jdesc);
- env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)lpAudioEffect);
+ setAudioEffect(env, thiz, lpAudioEffect);
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
@@ -407,12 +436,9 @@
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
- if (lpAudioEffect) {
- delete lpAudioEffect;
- }
- env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
-
if (lpJniStorage) {
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
}
env->SetLongField(thiz, fields.fidJniData, 0);
@@ -430,20 +456,20 @@
// ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
- ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
-
- // delete the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
- if (lpAudioEffect) {
- ALOGV("deleting AudioEffect: %p\n", lpAudioEffect);
- delete lpAudioEffect;
+static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
+ sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
+ if (lpAudioEffect == 0) {
+ return;
}
// delete the JNI data
- AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetLongField(
- thiz, fields.fidJniData);
+ AudioEffectJniStorage* lpJniStorage =
+ (AudioEffectJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+ // reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetLongField(thiz, fields.fidJniData, 0);
+
if (lpJniStorage) {
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
delete lpJniStorage;
@@ -451,24 +477,16 @@
}
// ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
-
- // do everything a call to finalize would
- android_media_AudioEffect_native_finalize(env, thiz);
- // + reset the native resources in the Java object so any attempt to access
- // them after a call to release fails.
- env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
- env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
+ android_media_AudioEffect_native_release(env, thiz);
}
static jint
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for enable()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -480,11 +498,8 @@
static jboolean
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getEnabled()");
return JNI_FALSE;
@@ -501,11 +516,8 @@
static jboolean
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for hasControl()");
return JNI_FALSE;
@@ -528,10 +540,8 @@
effect_param_t *p;
int voffset;
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -591,10 +601,8 @@
effect_param_t *p;
int voffset;
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -657,11 +665,8 @@
jbyte* pReplyData = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index abc681e..6098b4a4 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -102,14 +102,14 @@
};
// ----------------------------------------------------------------------------
-class visualizerJniStorage {
+class VisualizerJniStorage {
public:
visualizer_callback_cookie mCallbackData;
- visualizerJniStorage() {
+ VisualizerJniStorage() {
}
- ~visualizerJniStorage() {
+ ~VisualizerJniStorage() {
}
};
@@ -135,6 +135,7 @@
}
}
+static Mutex sLock;
// ----------------------------------------------------------------------------
static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -227,15 +228,30 @@
}
}
-static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
+// ----------------------------------------------------------------------------
+
+static sp<Visualizer> getVisualizer(JNIEnv* env, jobject thiz)
{
- Visualizer *v = (Visualizer *)env->GetLongField(
- thiz, fields.fidNativeVisualizer);
- if (v == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve Visualizer pointer");
+ Mutex::Autolock l(sLock);
+ Visualizer* const v =
+ (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+ return sp<Visualizer>(v);
+}
+
+static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
+ const sp<Visualizer>& v)
+{
+ Mutex::Autolock l(sLock);
+ sp<Visualizer> old =
+ (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+ if (v.get()) {
+ v->incStrong((void*)setVisualizer);
}
- return v;
+ if (old != 0) {
+ old->decStrong((void*)setVisualizer);
+ }
+ env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
+ return old;
}
// ----------------------------------------------------------------------------
@@ -318,7 +334,7 @@
void *info) {
if ((event == AudioEffect::EVENT_ERROR) &&
(*((status_t*)info) == DEAD_OBJECT)) {
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user;
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -336,14 +352,16 @@
jint sessionId, jintArray jId, jstring opPackageName)
{
ALOGV("android_media_visualizer_native_setup");
- visualizerJniStorage* lpJniStorage = NULL;
+ VisualizerJniStorage* lpJniStorage = NULL;
int lStatus = VISUALIZER_ERROR_NO_MEMORY;
- Visualizer* lpVisualizer = NULL;
+ sp<Visualizer> lpVisualizer;
jint* nId = NULL;
ScopedUtfChars opPackageNameStr(env, opPackageName);
- lpJniStorage = new visualizerJniStorage();
+ setVisualizer(env, thiz, 0);
+
+ lpJniStorage = new VisualizerJniStorage();
if (lpJniStorage == NULL) {
ALOGE("setup: Error creating JNI Storage");
goto setup_failure;
@@ -371,7 +389,7 @@
android_media_visualizer_effect_callback,
lpJniStorage,
sessionId);
- if (lpVisualizer == NULL) {
+ if (lpVisualizer == 0) {
ALOGE("Error creating Visualizer");
goto setup_failure;
}
@@ -392,7 +410,7 @@
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
- env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)lpVisualizer);
+ setVisualizer(env, thiz, lpVisualizer);
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
@@ -405,12 +423,9 @@
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
- if (lpVisualizer) {
- delete lpVisualizer;
- }
- env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
-
if (lpJniStorage) {
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
delete lpJniStorage;
}
env->SetLongField(thiz, fields.fidJniData, 0);
@@ -419,49 +434,44 @@
}
// ----------------------------------------------------------------------------
-static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
- ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
-
- // delete the Visualizer object
- Visualizer* lpVisualizer = (Visualizer *)env->GetLongField(
- thiz, fields.fidNativeVisualizer);
- if (lpVisualizer) {
- ALOGV("deleting Visualizer: %p\n", lpVisualizer);
- delete lpVisualizer;
+static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
+ sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
+ if (lpVisualizer == 0) {
+ return;
}
// delete the JNI data
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
- thiz, fields.fidJniData);
+ VisualizerJniStorage* lpJniStorage =
+ (VisualizerJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+ // reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetLongField(thiz, fields.fidJniData, 0);
+
if (lpJniStorage) {
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
delete lpJniStorage;
}
}
-// ----------------------------------------------------------------------------
-static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
-
- // do everything a call to finalize would
- android_media_visualizer_native_finalize(env, thiz);
- // + reset the native resources in the Java object so any attempt to access
- // them after a call to release fails.
- env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
- env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
+ android_media_visualizer_native_release(env, thiz);
}
+// ----------------------------------------------------------------------------
static jint
android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
jint retVal = translateError(lpVisualizer->setEnabled(enabled));
if (!enabled) {
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(
thiz, fields.fidJniData);
if (NULL != lpJniStorage)
@@ -474,8 +484,8 @@
static jboolean
android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return JNI_FALSE;
}
@@ -507,8 +517,8 @@
static jint
android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -518,8 +528,8 @@
static jint
android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint) lpVisualizer->getCaptureSize();
@@ -528,8 +538,8 @@
static jint
android_media_visualizer_native_setScalingMode(JNIEnv *env, jobject thiz, jint mode)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -539,8 +549,8 @@
static jint
android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint)lpVisualizer->getScalingMode();
@@ -549,8 +559,8 @@
static jint
android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
return translateError(lpVisualizer->setMeasurementMode(mode));
@@ -559,8 +569,8 @@
static jint
android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return MEASUREMENT_MODE_NONE;
}
return lpVisualizer->getMeasurementMode();
@@ -569,8 +579,8 @@
static jint
android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint) lpVisualizer->getSamplingRate();
@@ -579,8 +589,8 @@
static jint
android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -597,8 +607,8 @@
static jint
android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -616,8 +626,8 @@
static jint
android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
int32_t measurements[2];
@@ -635,11 +645,11 @@
static jint
android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(thiz,
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(thiz,
fields.fidJniData);
if (lpJniStorage == NULL) {
return VISUALIZER_ERROR_NO_INIT;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 3c76115..359a7a97a 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.os.SystemProperties;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
@@ -1497,7 +1498,12 @@
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
- view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
createEglContext = false;
}
@@ -1508,7 +1514,12 @@
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
- view.mRenderer.onSurfaceChanged(gl, w, h);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");
+ view.mRenderer.onSurfaceChanged(gl, w, h);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
sizeChanged = false;
}
@@ -1519,7 +1530,12 @@
{
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
- view.mRenderer.onDrawFrame(gl);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
+ view.mRenderer.onDrawFrame(gl);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
}
int swapError = mEglHelper.swap();
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index d876264..ddbcd78 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -336,12 +336,24 @@
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- Log.w(TAG, "SSL error; displaying SSL warning.");
+ Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
+ // Only show host to avoid leaking private info.
+ Uri.parse(error.getUrl()).getHost() + " certificate: " +
+ error.getCertificate() + "); displaying SSL warning.");
final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
getString(R.string.ssl_error_example), mBrowserBailOutToken,
getString(R.string.ssl_error_continue));
view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
}
+
+ @Override
+ public boolean shouldOverrideUrlLoading (WebView view, String url) {
+ if (url.startsWith("tel:")) {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
+ return true;
+ }
+ return false;
+ }
}
private class MyWebChromeClient extends WebChromeClient {
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
index 95b2b6d..67f890c 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index ddf9a80..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
index 7168197..1a9cd75 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 1c6ec6a..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
index 11546a743..4c3d9a4 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
index de5c113..da56077 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
index 769144a..2b21c12 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
index 22f95b9..ed3ee45 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
index c5152f4..1a3ebc47 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png
deleted file mode 100644
index eb71f81..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
index 4997173..9a9e570 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 59eef4d..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
index cd8987a..1d25a2d 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 95716b3..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 76394c4..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
index 941b93c..c593e7a 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 4f5bcea..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
index fad19c1..5e66488 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index df94033..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
index 87154ad..7e15a8c 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
index 9b40dae..c15537a 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png
deleted file mode 100644
index 6b0d909..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png
deleted file mode 100644
index e1c39f8..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
index 092556a..d9aacea 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 223e619..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
index 37fbdf7..9e003f0 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
index 0929f93..65e42aa 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 738ef84..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
index 897f7a9..9a048f1 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index c9224a6..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
index 3bb74f9..40a1a84 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 2d47376..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
index 3868210..e768d11 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
index a74854d..5ef3dc0 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
index f5e45a7..ac27eea 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
index 67e33ce..a4add51 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
index 34093d0..a9a7f20 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png
deleted file mode 100644
index 5ecddc5..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
index f8a5262..c94cc28 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index d8ebf49..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
index 62c47d5..6e6b870 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 0825647..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 9d9e3d7..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
index 9fc1df6..6b16343 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index c95381b..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
index f95d30d..04a12a4 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 47c00dd..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
index fe2c78c..5f968d5 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
index 8b3a303..7a8eae9 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png
deleted file mode 100644
index 5923c08..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 0e45037..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
index a55f4ca..c2c845e 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 993e060..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
index f359c4be..f500d58 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
index dd76d07..1de8292 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index fccd45e..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
index 5d0805e..073583e 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index 1a3d7c4..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
index 14b9aef..6bc4372 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 953ab62..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
index 5198b84..2ea6164 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
index cdf8c95..46ed12a 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
index aecd4cf..4203d35 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
index 24ad2dd..41558f2 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
index bf9dbde..e190c4d 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png
deleted file mode 100644
index cd24dfe..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
index 9afd1a1..1cf76a9 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 95b0bb83..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
index 14e96e2..49272b0 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 311e4dc..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index e2e3925..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
index 6b2ddad..6381902 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 304435a..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
index 27e6acf..9e4fd61 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 146b196..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
index cc4d40c..630188c 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
index 6c897c4..73372f4 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png
deleted file mode 100644
index be6fb5d..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 7ba284f..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
index 9bda62c..f5afb24 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 24dce60..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
index 5ef3288..fe71c25 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
index 99b8054..00b8a8b7 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 80717e4..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
index 02a988b..b96cfd1 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index a23a9ae..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
index b10db3e..51b4401 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 3606fb3..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
index 0a54598..ed36f70 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
index 6d3260c..a81eeb9 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
index 31dc7a4..60f59f5 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
index 5d0f0db..6006b12 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
index 8116b62..7926188 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png
deleted file mode 100644
index 02249d2..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
index 90d1a09..074ea88 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 7a993b9..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
index 12d279b..5c4360a 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 325c48a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index ee2465d..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
index 538fbe5..3ae490e 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index d99206c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
index 4f1355c..cb9d196 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index d5cc56f..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
index de96c3f..7560f62 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
index 7805834..b9483c3 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png
deleted file mode 100644
index 7ed919c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 756b684..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
index ffb9841..ce97c85 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 305a6b8..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
index dc1425d..1dad2a8 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
index cea8ac0..5236774 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index e0a1d23a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
index 84573f6..6f2dc5b 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index 986d3fb..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
index ce443f9..df42fee 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index a54e0ea..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
index 6e65716..3f4d539 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
index 35bd56e..c8f86b9 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
index 64aa50b..d2dd494 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
index 7cbcb8b..4f935bf 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
index d6d79ec..7499cbc 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png
deleted file mode 100644
index c0c27a2..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
index 2dcf03e..1f6af72 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index d7eb04f..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
index 879bf45..073d8533 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 2cd4137..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 268049e..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
index 78fc61d..21be572 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 36e8e68..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
index 827a92c..631663aa 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 0f12b7d..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
index df1bac1..5d1e8d9 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
index 9420374..7c1506b 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png
deleted file mode 100644
index 0394c5c..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 325d374..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
index 87a5210..8c83bff 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index bb41a91..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
index baa723d..68df974 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
index 39c737f..c7daa2a 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 88973e0..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_cab_accept.xml b/packages/DocumentsUI/res/drawable/ic_cab_accept.xml
deleted file mode 100644
index cda28453..0000000
--- a/packages/DocumentsUI/res/drawable/ic_cab_accept.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_cab_accept_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml b/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml
deleted file mode 100644
index e440ee9..0000000
--- a/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_cab_select_item_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_folder.xml b/packages/DocumentsUI/res/drawable/ic_folder.xml
deleted file mode 100644
index 8adaf3b..0000000
--- a/packages/DocumentsUI/res/drawable/ic_folder.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_folder_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml b/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml
deleted file mode 100644
index 15c5c39..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_disconnect_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml b/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml
deleted file mode 100644
index a579b60..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_overflow_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_rename.xml b/packages/DocumentsUI/res/drawable/ic_menu_rename.xml
deleted file mode 100644
index b5ba30c..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_rename.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_rename_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_settings.xml b/packages/DocumentsUI/res/drawable/ic_menu_settings.xml
deleted file mode 100644
index 7606e75..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_settings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_settings_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_undo.xml b/packages/DocumentsUI/res/drawable/ic_menu_undo.xml
deleted file mode 100644
index d66ba6d..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_undo.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_undo_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_open.xml b/packages/DocumentsUI/res/drawable/ic_open.xml
deleted file mode 100644
index e3f1faf..0000000
--- a/packages/DocumentsUI/res/drawable/ic_open.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_open_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_popout.xml b/packages/DocumentsUI/res/drawable/ic_popout.xml
deleted file mode 100644
index e7d0f75..0000000
--- a/packages/DocumentsUI/res/drawable/ic_popout.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_popout_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_root_folder.xml b/packages/DocumentsUI/res/drawable/ic_root_folder.xml
deleted file mode 100644
index 01df07b..0000000
--- a/packages/DocumentsUI/res/drawable/ic_root_folder.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_root_folder_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml b/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml
deleted file mode 100644
index 5f80dd9..0000000
--- a/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_root_usb_light_alpha"
- android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 7b40a0f..a9f1b3c 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -41,7 +41,9 @@
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
- android:baselineAligned="false">
+ android:baselineAligned="false"
+ android:divider="?android:attr/dividerVertical"
+ android:showDividers="middle">
<FrameLayout
android:id="@+id/container_roots"
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3c30d8c..e6a89f1 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -205,7 +205,7 @@
handleScreenTurnedOn();
break;
case MSG_FINGERPRINT_AUTHENTICATED:
- handleFingerprintAuthenticated(msg.arg1, msg.arg2);
+ handleFingerprintAuthenticated();
break;
case MSG_FINGERPRINT_HELP:
handleFingerprintHelp(msg.arg1 /* msgId */, (String) msg.obj /* errString */);
@@ -348,7 +348,7 @@
handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
}
- private void handleFingerprintAuthenticated(int fingerId, int groupId) {
+ private void handleFingerprintAuthenticated() {
try {
final int userId;
try {
@@ -524,9 +524,7 @@
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
- mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED,
- result.getFingerprint().getFingerId(),
- result.getFingerprint().getGroupId()).sendToTarget();
+ mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
}
@Override
@@ -796,7 +794,7 @@
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0, userId);
+ mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
setFingerprintRunningDetectionRunning(true);
}
}
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index eacafd5..3873593 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -16,5 +16,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib">
+ package="com.android.settingslib">
+
+ <uses-sdk
+ android:minSdkVersion="21" />
+
</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
new file mode 100644
index 0000000..b5e53c0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -0,0 +1,1347 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.text.Collator;
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Keeps track of information about all installed applications, lazy-loading
+ * as needed.
+ */
+public class ApplicationsState {
+ static final String TAG = "ApplicationsState";
+ static final boolean DEBUG = false;
+ static final boolean DEBUG_LOCKING = false;
+
+ public static final int SIZE_UNKNOWN = -1;
+ public static final int SIZE_INVALID = -2;
+
+ static final Pattern REMOVE_DIACRITICALS_PATTERN
+ = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+
+ static final Object sLock = new Object();
+ static ApplicationsState sInstance;
+
+ public static ApplicationsState getInstance(Application app) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new ApplicationsState(app);
+ }
+ return sInstance;
+ }
+ }
+
+ final Context mContext;
+ final PackageManager mPm;
+ final IPackageManager mIpm;
+ final UserManager mUm;
+ final int mOwnerRetrieveFlags;
+ final int mRetrieveFlags;
+ PackageIntentReceiver mPackageIntentReceiver;
+
+ boolean mResumed;
+ boolean mHaveDisabledApps;
+
+ // Information about all applications. Synchronize on mEntriesMap
+ // to protect access to these.
+ final ArrayList<Session> mSessions = new ArrayList<Session>();
+ final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
+ final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
+ // Map: userid => (Map: package name => AppEntry)
+ final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
+ new SparseArray<HashMap<String, AppEntry>>();
+ final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
+ List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
+ long mCurId = 1;
+ String mCurComputingSizePkg;
+ int mCurComputingSizeUserId;
+ boolean mSessionsChanged;
+
+ // Temporary for dispatching session callbacks. Only touched by main thread.
+ final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
+
+ final HandlerThread mThread;
+ final BackgroundHandler mBackgroundHandler;
+ final MainHandler mMainHandler = new MainHandler();
+
+ private ApplicationsState(Application app) {
+ mContext = app;
+ mPm = mContext.getPackageManager();
+ mIpm = AppGlobals.getPackageManager();
+ mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
+ for (UserHandle user : mUm.getUserProfiles()) {
+ mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
+ }
+ mThread = new HandlerThread("ApplicationsState.Loader",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mThread.start();
+ mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
+
+ // Only the owner can see all apps.
+ mOwnerRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
+ PackageManager.GET_DISABLED_COMPONENTS |
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
+ mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS |
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
+
+ /**
+ * This is a trick to prevent the foreground thread from being delayed.
+ * The problem is that Dalvik monitors are initially spin locks, to keep
+ * them lightweight. This leads to unfair contention -- Even though the
+ * background thread only holds the lock for a short amount of time, if
+ * it keeps running and locking again it can prevent the main thread from
+ * acquiring its lock for a long time... sometimes even > 5 seconds
+ * (leading to an ANR).
+ *
+ * Dalvik will promote a monitor to a "real" lock if it detects enough
+ * contention on it. It doesn't figure this out fast enough for us
+ * here, though, so this little trick will force it to turn into a real
+ * lock immediately.
+ */
+ synchronized (mEntriesMap) {
+ try {
+ mEntriesMap.wait(1);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public Looper getBackgroundLooper() {
+ return mThread.getLooper();
+ }
+
+ public Session newSession(Callbacks callbacks) {
+ Session s = new Session(callbacks);
+ synchronized (mEntriesMap) {
+ mSessions.add(s);
+ }
+ return s;
+ }
+
+ void doResumeIfNeededLocked() {
+ if (mResumed) {
+ return;
+ }
+ mResumed = true;
+ if (mPackageIntentReceiver == null) {
+ mPackageIntentReceiver = new PackageIntentReceiver();
+ mPackageIntentReceiver.registerReceiver();
+ }
+ mApplications = new ArrayList<ApplicationInfo>();
+ for (UserHandle user : mUm.getUserProfiles()) {
+ try {
+ // If this user is new, it needs a map created.
+ if (mEntriesMap.indexOfKey(user.getIdentifier()) < 0) {
+ mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
+ }
+ @SuppressWarnings("unchecked")
+ ParceledListSlice<ApplicationInfo> list =
+ mIpm.getInstalledApplications(
+ user.isOwner() ? mOwnerRetrieveFlags : mRetrieveFlags,
+ user.getIdentifier());
+ mApplications.addAll(list.getList());
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+ // If an interesting part of the configuration has changed, we
+ // should completely reload the app entries.
+ clearEntries();
+ } else {
+ for (int i=0; i<mAppEntries.size(); i++) {
+ mAppEntries.get(i).sizeStale = true;
+ }
+ }
+
+ mHaveDisabledApps = false;
+ for (int i=0; i<mApplications.size(); i++) {
+ final ApplicationInfo info = mApplications.get(i);
+ // Need to trim out any applications that are disabled by
+ // something different than the user.
+ if (!info.enabled) {
+ if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ mApplications.remove(i);
+ i--;
+ continue;
+ }
+ mHaveDisabledApps = true;
+ }
+ int userId = UserHandle.getUserId(info.uid);
+ final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
+ if (entry != null) {
+ entry.info = info;
+ }
+ }
+ if (mAppEntries.size() > mApplications.size()) {
+ // There are less apps now, some must have been uninstalled.
+ clearEntries();
+ }
+ mCurComputingSizePkg = null;
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+ mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+ }
+ }
+
+ private void clearEntries() {
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ mEntriesMap.valueAt(i).clear();
+ }
+ mAppEntries.clear();
+ }
+
+ public boolean haveDisabledApps() {
+ return mHaveDisabledApps;
+ }
+
+ void doPauseIfNeededLocked() {
+ if (!mResumed) {
+ return;
+ }
+ for (int i=0; i<mSessions.size(); i++) {
+ if (mSessions.get(i).mResumed) {
+ return;
+ }
+ }
+ doPauseLocked();
+ }
+
+ void doPauseLocked() {
+ mResumed = false;
+ if (mPackageIntentReceiver != null) {
+ mPackageIntentReceiver.unregisterReceiver();
+ mPackageIntentReceiver = null;
+ }
+ }
+
+ public AppEntry getEntry(String packageName, int userId) {
+ if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
+ synchronized (mEntriesMap) {
+ AppEntry entry = mEntriesMap.get(userId).get(packageName);
+ if (entry == null) {
+ ApplicationInfo info = getAppInfoLocked(packageName, userId);
+ if (info == null) {
+ try {
+ info = mIpm.getApplicationInfo(packageName, 0, userId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "getEntry couldn't reach PackageManager", e);
+ return null;
+ }
+ }
+ entry = getEntryLocked(info);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
+ return entry;
+ }
+ }
+
+ private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
+ for (int i = 0; i < mApplications.size(); i++) {
+ ApplicationInfo info = mApplications.get(i);
+ if (pkg.equals(info.packageName)
+ && userId == UserHandle.getUserId(info.uid)) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ public void ensureIcon(AppEntry entry) {
+ if (entry.icon != null) {
+ return;
+ }
+ synchronized (entry) {
+ entry.ensureIconLocked(mContext, mPm);
+ }
+ }
+
+ public void requestSize(String packageName, int userId) {
+ if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
+ synchronized (mEntriesMap) {
+ AppEntry entry = mEntriesMap.get(userId).get(packageName);
+ if (entry != null) {
+ mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
+ }
+ }
+
+ long sumCacheSizes() {
+ long sum = 0;
+ if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
+ for (int i=mAppEntries.size()-1; i>=0; i--) {
+ sum += mAppEntries.get(i).cacheSize;
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
+ }
+ return sum;
+ }
+
+ int indexOfApplicationInfoLocked(String pkgName, int userId) {
+ for (int i=mApplications.size()-1; i>=0; i--) {
+ ApplicationInfo appInfo = mApplications.get(i);
+ if (appInfo.packageName.equals(pkgName)
+ && UserHandle.getUserId(appInfo.uid) == userId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ void addPackage(String pkgName, int userId) {
+ try {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
+ if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
+ if (!mResumed) {
+ // If we are not resumed, we will do a full query the
+ // next time we resume, so there is no reason to do work
+ // here.
+ if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
+ return;
+ }
+ if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
+ if (DEBUG) Log.i(TAG, "Package already exists!");
+ if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
+ return;
+ }
+ ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
+ userId == UserHandle.USER_OWNER ? mOwnerRetrieveFlags : mRetrieveFlags,
+ userId);
+ if (info == null) {
+ return;
+ }
+ if (!info.enabled) {
+ if (info.enabledSetting
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ return;
+ }
+ mHaveDisabledApps = true;
+ }
+ mApplications.add(info);
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+ mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void removePackage(String pkgName, int userId) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
+ int idx = indexOfApplicationInfoLocked(pkgName, userId);
+ if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
+ if (idx >= 0) {
+ AppEntry entry = mEntriesMap.get(userId).get(pkgName);
+ if (DEBUG) Log.i(TAG, "removePackage: " + entry);
+ if (entry != null) {
+ mEntriesMap.get(userId).remove(pkgName);
+ mAppEntries.remove(entry);
+ }
+ ApplicationInfo info = mApplications.get(idx);
+ mApplications.remove(idx);
+ if (!info.enabled) {
+ mHaveDisabledApps = false;
+ for (int i=0; i<mApplications.size(); i++) {
+ if (!mApplications.get(i).enabled) {
+ mHaveDisabledApps = true;
+ break;
+ }
+ }
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
+ }
+ }
+
+ public void invalidatePackage(String pkgName, int userId) {
+ removePackage(pkgName, userId);
+ addPackage(pkgName, userId);
+ }
+
+ private void addUser(int userId) {
+ if (mUm.getUserProfiles().contains(new UserHandle(userId))) {
+ synchronized (mEntriesMap) {
+ mEntriesMap.put(userId, new HashMap<String, AppEntry>());
+ if (mResumed) {
+ // If resumed, Manually pause, then cause a resume to repopulate the app list.
+ // This is the simplest way to reload the packages so that the new user
+ // is included. Otherwise the list will be repopulated on next resume.
+ doPauseLocked();
+ doResumeIfNeededLocked();
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ }
+ }
+ }
+
+ private void removeUser(int userId) {
+ synchronized (mEntriesMap) {
+ HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
+ if (userMap != null) {
+ for (AppEntry appEntry : userMap.values()) {
+ mAppEntries.remove(appEntry);
+ mApplications.remove(appEntry.info);
+ }
+ mEntriesMap.remove(userId);
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ }
+ }
+ }
+
+ private AppEntry getEntryLocked(ApplicationInfo info) {
+ int userId = UserHandle.getUserId(info.uid);
+ AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
+ if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
+ if (entry == null) {
+ if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
+ entry = new AppEntry(mContext, info, mCurId++);
+ mEntriesMap.get(userId).put(info.packageName, entry);
+ mAppEntries.add(entry);
+ } else if (entry.info != info) {
+ entry.info = info;
+ }
+ return entry;
+ }
+
+ // --------------------------------------------------------------
+
+ private long getTotalInternalSize(PackageStats ps) {
+ if (ps != null) {
+ return ps.codeSize + ps.dataSize;
+ }
+ return SIZE_INVALID;
+ }
+
+ private long getTotalExternalSize(PackageStats ps) {
+ if (ps != null) {
+ // We also include the cache size here because for non-emulated
+ // we don't automtically clean cache files.
+ return ps.externalCodeSize + ps.externalDataSize
+ + ps.externalCacheSize
+ + ps.externalMediaSize + ps.externalObbSize;
+ }
+ return SIZE_INVALID;
+ }
+
+ private String getSizeStr(long size) {
+ if (size >= 0) {
+ return Formatter.formatFileSize(mContext, size);
+ }
+ return null;
+ }
+
+ void rebuildActiveSessions() {
+ synchronized (mEntriesMap) {
+ if (!mSessionsChanged) {
+ return;
+ }
+ mActiveSessions.clear();
+ for (int i=0; i<mSessions.size(); i++) {
+ Session s = mSessions.get(i);
+ if (s.mResumed) {
+ mActiveSessions.add(s);
+ }
+ }
+ }
+ }
+
+ public static String normalize(String str) {
+ String tmp = Normalizer.normalize(str, Form.NFD);
+ return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
+ .replaceAll("").toLowerCase();
+ }
+
+ public class Session {
+ final Callbacks mCallbacks;
+ boolean mResumed;
+
+ // Rebuilding of app list. Synchronized on mRebuildSync.
+ final Object mRebuildSync = new Object();
+ boolean mRebuildRequested;
+ boolean mRebuildAsync;
+ AppFilter mRebuildFilter;
+ Comparator<AppEntry> mRebuildComparator;
+ ArrayList<AppEntry> mRebuildResult;
+ ArrayList<AppEntry> mLastAppList;
+
+ Session(Callbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ public void resume() {
+ if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
+ synchronized (mEntriesMap) {
+ if (!mResumed) {
+ mResumed = true;
+ mSessionsChanged = true;
+ doResumeIfNeededLocked();
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
+ }
+
+ public void pause() {
+ if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
+ synchronized (mEntriesMap) {
+ if (mResumed) {
+ mResumed = false;
+ mSessionsChanged = true;
+ mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
+ doPauseIfNeededLocked();
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
+ }
+ }
+
+ public ArrayList<AppEntry> getAllApps() {
+ synchronized (mEntriesMap) {
+ return new ArrayList<>(mAppEntries);
+ }
+ }
+
+ // Creates a new list of app entries with the given filter and comparator.
+ public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+ synchronized (mRebuildSync) {
+ synchronized (mEntriesMap) {
+ mRebuildingSessions.add(this);
+ mRebuildRequested = true;
+ mRebuildAsync = false;
+ mRebuildFilter = filter;
+ mRebuildComparator = comparator;
+ mRebuildResult = null;
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
+ Message msg = mBackgroundHandler.obtainMessage(
+ BackgroundHandler.MSG_REBUILD_LIST);
+ mBackgroundHandler.sendMessage(msg);
+ }
+ }
+
+ // We will wait for .25s for the list to be built.
+ long waitend = SystemClock.uptimeMillis()+250;
+
+ while (mRebuildResult == null) {
+ long now = SystemClock.uptimeMillis();
+ if (now >= waitend) {
+ break;
+ }
+ try {
+ mRebuildSync.wait(waitend - now);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ mRebuildAsync = true;
+
+ return mRebuildResult;
+ }
+ }
+
+ void handleRebuildList() {
+ AppFilter filter;
+ Comparator<AppEntry> comparator;
+ synchronized (mRebuildSync) {
+ if (!mRebuildRequested) {
+ return;
+ }
+
+ filter = mRebuildFilter;
+ comparator = mRebuildComparator;
+ mRebuildRequested = false;
+ mRebuildFilter = null;
+ mRebuildComparator = null;
+ }
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+ if (filter != null) {
+ filter.init();
+ }
+
+ List<AppEntry> apps;
+ synchronized (mEntriesMap) {
+ apps = new ArrayList<>(mAppEntries);
+ }
+
+ ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
+ if (DEBUG) Log.i(TAG, "Rebuilding...");
+ for (int i=0; i<apps.size(); i++) {
+ AppEntry entry = apps.get(i);
+ if (filter == null || filter.filterApp(entry)) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
+ entry.ensureLabel(mContext);
+ if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
+ filteredApps.add(entry);
+ if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
+ }
+ }
+ }
+
+ Collections.sort(filteredApps, comparator);
+
+ synchronized (mRebuildSync) {
+ if (!mRebuildRequested) {
+ mLastAppList = filteredApps;
+ if (!mRebuildAsync) {
+ mRebuildResult = filteredApps;
+ mRebuildSync.notifyAll();
+ } else {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
+ Message msg = mMainHandler.obtainMessage(
+ MainHandler.MSG_REBUILD_COMPLETE, this);
+ mMainHandler.sendMessage(msg);
+ }
+ }
+ }
+ }
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
+ public void release() {
+ pause();
+ synchronized (mEntriesMap) {
+ mSessions.remove(this);
+ }
+ }
+ }
+
+ class MainHandler extends Handler {
+ static final int MSG_REBUILD_COMPLETE = 1;
+ static final int MSG_PACKAGE_LIST_CHANGED = 2;
+ static final int MSG_PACKAGE_ICON_CHANGED = 3;
+ static final int MSG_PACKAGE_SIZE_CHANGED = 4;
+ static final int MSG_ALL_SIZES_COMPUTED = 5;
+ static final int MSG_RUNNING_STATE_CHANGED = 6;
+ static final int MSG_LAUNCHER_INFO_CHANGED = 7;
+ static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
+
+ @Override
+ public void handleMessage(Message msg) {
+ rebuildActiveSessions();
+ switch (msg.what) {
+ case MSG_REBUILD_COMPLETE: {
+ Session s = (Session)msg.obj;
+ if (mActiveSessions.contains(s)) {
+ s.mCallbacks.onRebuildComplete(s.mLastAppList);
+ }
+ } break;
+ case MSG_PACKAGE_LIST_CHANGED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageListChanged();
+ }
+ } break;
+ case MSG_PACKAGE_ICON_CHANGED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
+ }
+ } break;
+ case MSG_PACKAGE_SIZE_CHANGED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
+ (String)msg.obj);
+ }
+ } break;
+ case MSG_ALL_SIZES_COMPUTED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
+ }
+ } break;
+ case MSG_RUNNING_STATE_CHANGED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
+ msg.arg1 != 0);
+ }
+ } break;
+ case MSG_LAUNCHER_INFO_CHANGED: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
+ }
+ } break;
+ case MSG_LOAD_ENTRIES_COMPLETE: {
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
+ }
+ } break;
+ }
+ }
+ }
+
+ private class BackgroundHandler extends Handler {
+ static final int MSG_REBUILD_LIST = 1;
+ static final int MSG_LOAD_ENTRIES = 2;
+ static final int MSG_LOAD_ICONS = 3;
+ static final int MSG_LOAD_SIZES = 4;
+ static final int MSG_LOAD_LAUNCHER = 5;
+
+ boolean mRunning;
+
+ BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ // Always try rebuilding list first thing, if needed.
+ ArrayList<Session> rebuildingSessions = null;
+ synchronized (mEntriesMap) {
+ if (mRebuildingSessions.size() > 0) {
+ rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
+ mRebuildingSessions.clear();
+ }
+ }
+ if (rebuildingSessions != null) {
+ for (int i=0; i<rebuildingSessions.size(); i++) {
+ rebuildingSessions.get(i).handleRebuildList();
+ }
+ }
+
+ switch (msg.what) {
+ case MSG_REBUILD_LIST: {
+ } break;
+ case MSG_LOAD_ENTRIES: {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
+ for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ ApplicationInfo info = mApplications.get(i);
+ int userId = UserHandle.getUserId(info.uid);
+ if (mEntriesMap.get(userId).get(info.packageName) == null) {
+ numDone++;
+ getEntryLocked(info);
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
+ }
+
+ if (numDone >= 6) {
+ sendEmptyMessage(MSG_LOAD_ENTRIES);
+ } else {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
+ }
+ sendEmptyMessage(MSG_LOAD_LAUNCHER);
+ }
+ } break;
+ case MSG_LOAD_LAUNCHER: {
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
+ .addCategory(Intent.CATEGORY_LAUNCHER);
+
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ int userId = mEntriesMap.keyAt(i);
+ List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(launchIntent,
+ PackageManager.GET_DISABLED_COMPONENTS, userId);
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+ HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+ final int N = intents.size();
+ for (int j = 0; j < N; j++) {
+ String packageName = intents.get(j).activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.hasLauncherEntry = true;
+ } else {
+ Log.w(TAG, "Cannot find pkg: " + packageName
+ + " on user " + userId);
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+ }
+ }
+
+ if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+ }
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ } break;
+ case MSG_LOAD_ICONS: {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+ for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.icon == null || !entry.mounted) {
+ synchronized (entry) {
+ if (entry.ensureIconLocked(mContext, mPm)) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ numDone++;
+ }
+ }
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+ }
+ if (numDone > 0) {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+ }
+ }
+ if (numDone >= 2) {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ } else {
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ }
+ } break;
+ case MSG_LOAD_SIZES: {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+ if (mCurComputingSizePkg != null) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ for (int i=0; i<mAppEntries.size(); i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
+ if (entry.sizeLoadStart == 0 ||
+ (entry.sizeLoadStart < (now-20*1000))) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ entry.sizeLoadStart = now;
+ mCurComputingSizePkg = entry.info.packageName;
+ mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
+ mPm.getPackageSizeInfo(mCurComputingSizePkg,
+ mCurComputingSizeUserId, mStatsObserver);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
+ return;
+ }
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+ mRunning = false;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+ mMainHandler.sendMessage(m);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
+ }
+ } break;
+ }
+ }
+
+ final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ boolean sizeChanged = false;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
+ HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
+ if (userMap == null) {
+ // The user must have been removed.
+ return;
+ }
+ AppEntry entry = userMap.get(stats.packageName);
+ if (entry != null) {
+ synchronized (entry) {
+ entry.sizeStale = false;
+ entry.sizeLoadStart = 0;
+ long externalCodeSize = stats.externalCodeSize
+ + stats.externalObbSize;
+ long externalDataSize = stats.externalDataSize
+ + stats.externalMediaSize;
+ long newSize = externalCodeSize + externalDataSize
+ + getTotalInternalSize(stats);
+ if (entry.size != newSize ||
+ entry.cacheSize != stats.cacheSize ||
+ entry.codeSize != stats.codeSize ||
+ entry.dataSize != stats.dataSize ||
+ entry.externalCodeSize != externalCodeSize ||
+ entry.externalDataSize != externalDataSize ||
+ entry.externalCacheSize != stats.externalCacheSize) {
+ entry.size = newSize;
+ entry.cacheSize = stats.cacheSize;
+ entry.codeSize = stats.codeSize;
+ entry.dataSize = stats.dataSize;
+ entry.externalCodeSize = externalCodeSize;
+ entry.externalDataSize = externalDataSize;
+ entry.externalCacheSize = stats.externalCacheSize;
+ entry.sizeStr = getSizeStr(entry.size);
+ entry.internalSize = getTotalInternalSize(stats);
+ entry.internalSizeStr = getSizeStr(entry.internalSize);
+ entry.externalSize = getTotalExternalSize(stats);
+ entry.externalSizeStr = getSizeStr(entry.externalSize);
+ if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
+ + ": " + entry.sizeStr);
+ sizeChanged = true;
+ }
+ }
+ if (sizeChanged) {
+ Message msg = mMainHandler.obtainMessage(
+ MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
+ mMainHandler.sendMessage(msg);
+ }
+ }
+ if (mCurComputingSizePkg != null
+ && (mCurComputingSizePkg.equals(stats.packageName)
+ && mCurComputingSizeUserId == stats.userHandle)) {
+ mCurComputingSizePkg = null;
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
+ }
+ }
+ };
+ }
+
+ /**
+ * Receives notifications when applications are added/removed.
+ */
+ private class PackageIntentReceiver extends BroadcastReceiver {
+ void registerReceiver() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ // Register for events related to sdcard installation.
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(this, sdFilter);
+ // Register for events related to user creation/deletion.
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(this, userFilter);
+ }
+ void unregisterReceiver() {
+ mContext.unregisterReceiver(this);
+ }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String actionStr = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ addPackage(pkgName, mEntriesMap.keyAt(i));
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ removePackage(pkgName, mEntriesMap.keyAt(i));
+ }
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ invalidatePackage(pkgName, mEntriesMap.keyAt(i));
+ }
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
+ Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
+ // When applications become available or unavailable (perhaps because
+ // the SD card was inserted or ejected) we need to refresh the
+ // AppInfo with new label, icon and size information as appropriate
+ // given the newfound (un)availability of the application.
+ // A simple way to do that is to treat the refresh as a package
+ // removal followed by a package addition.
+ String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ if (pkgList == null || pkgList.length == 0) {
+ // Ignore
+ return;
+ }
+ boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
+ if (avail) {
+ for (String pkgName : pkgList) {
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ invalidatePackage(pkgName, mEntriesMap.keyAt(i));
+ }
+ }
+ }
+ } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
+ addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+ } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+ }
+ }
+ }
+
+ public interface Callbacks {
+ void onRunningStateChanged(boolean running);
+ void onPackageListChanged();
+ void onRebuildComplete(ArrayList<AppEntry> apps);
+ void onPackageIconChanged();
+ void onPackageSizeChanged(String packageName);
+ void onAllSizesComputed();
+ void onLauncherInfoChanged();
+ void onLoadEntriesCompleted();
+ }
+
+ public static class SizeInfo {
+ public long cacheSize;
+ public long codeSize;
+ public long dataSize;
+ public long externalCodeSize;
+ public long externalDataSize;
+
+ // This is the part of externalDataSize that is in the cache
+ // section of external storage. Note that we don't just combine
+ // this with cacheSize because currently the platform can't
+ // automatically trim this data when needed, so it is something
+ // the user may need to manage. The externalDataSize also includes
+ // this value, since what this is here is really the part of
+ // externalDataSize that we can just consider to be "cache" files
+ // for purposes of cleaning them up in the app details UI.
+ public long externalCacheSize;
+ }
+
+ public static class AppEntry extends SizeInfo {
+ public final File apkFile;
+ public final long id;
+ public String label;
+ public long size;
+ public long internalSize;
+ public long externalSize;
+
+ public boolean mounted;
+
+ public boolean hasLauncherEntry;
+
+ public String getNormalizedLabel() {
+ if (normalizedLabel != null) {
+ return normalizedLabel;
+ }
+ normalizedLabel = normalize(label);
+ return normalizedLabel;
+ }
+
+ // Need to synchronize on 'this' for the following.
+ public ApplicationInfo info;
+ public Drawable icon;
+ public String sizeStr;
+ public String internalSizeStr;
+ public String externalSizeStr;
+ public boolean sizeStale;
+ public long sizeLoadStart;
+
+ public String normalizedLabel;
+
+ // A location where extra info can be placed to be used by custom filters.
+ public Object extraInfo;
+
+ AppEntry(Context context, ApplicationInfo info, long id) {
+ apkFile = new File(info.sourceDir);
+ this.id = id;
+ this.info = info;
+ this.size = SIZE_UNKNOWN;
+ this.sizeStale = true;
+ ensureLabel(context);
+ }
+
+ public void ensureLabel(Context context) {
+ if (this.label == null || !this.mounted) {
+ if (!this.apkFile.exists()) {
+ this.mounted = false;
+ this.label = info.packageName;
+ } else {
+ this.mounted = true;
+ CharSequence label = info.loadLabel(context.getPackageManager());
+ this.label = label != null ? label.toString() : info.packageName;
+ }
+ }
+ }
+
+ boolean ensureIconLocked(Context context, PackageManager pm) {
+ if (this.icon == null) {
+ if (this.apkFile.exists()) {
+ this.icon = getBadgedIcon(pm);
+ return true;
+ } else {
+ this.mounted = false;
+ this.icon = context.getDrawable(
+ com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+ }
+ } else if (!this.mounted) {
+ // If the app wasn't mounted but is now mounted, reload
+ // its icon.
+ if (this.apkFile.exists()) {
+ this.mounted = true;
+ this.icon = getBadgedIcon(pm);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Drawable getBadgedIcon(PackageManager pm) {
+ // Do badging ourself so that it comes from the user of the app not the current user.
+ return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
+ new UserHandle(UserHandle.getUserId(info.uid)));
+ }
+
+ public String getVersion(Context context) {
+ try {
+ return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ return "";
+ }
+ }
+ }
+
+ public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public static final Comparator<AppEntry> SIZE_COMPARATOR
+ = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ if (object1.size < object2.size) return 1;
+ if (object1.size > object2.size) return -1;
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
+ = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ if (object1.internalSize < object2.internalSize) return 1;
+ if (object1.internalSize > object2.internalSize) return -1;
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
+ = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ if (object1.externalSize < object2.externalSize) return 1;
+ if (object1.externalSize > object2.externalSize) return -1;
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public interface AppFilter {
+ void init();
+ boolean filterApp(AppEntry info);
+ }
+
+ public static final AppFilter FILTER_PERSONAL = new AppFilter() {
+ private int mCurrentUser;
+
+ public void init() {
+ mCurrentUser = ActivityManager.getCurrentUser();
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
+ }
+ };
+
+ public static final AppFilter FILTER_WORK = new AppFilter() {
+ private int mCurrentUser;
+
+ public void init() {
+ mCurrentUser = ActivityManager.getCurrentUser();
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
+ }
+ };
+
+ public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ return true;
+ } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return true;
+ } else if (entry.hasLauncherEntry) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ return true;
+ } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ public static final AppFilter FILTER_DISABLED = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return !entry.info.enabled;
+ }
+ };
+
+ public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return entry.info.enabled;
+ }
+ };
+
+ public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return true;
+ }
+ };
+
+ public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ }
+ };
+
+ public static class VolumeFilter implements AppFilter {
+ private final String mVolumeUuid;
+
+ public VolumeFilter(String volumeUuid) {
+ mVolumeUuid = volumeUuid;
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry info) {
+ return Objects.equals(info.info.volumeUuid, mVolumeUuid);
+ }
+ }
+
+ public static class CompoundFilter implements AppFilter {
+ private final AppFilter mFirstFilter;
+ private final AppFilter mSecondFilter;
+
+ public CompoundFilter(AppFilter first, AppFilter second) {
+ mFirstFilter = first;
+ mSecondFilter = second;
+ }
+
+ @Override
+ public void init() {
+ mFirstFilter.init();
+ mSecondFilter.init();
+ }
+
+ @Override
+ public boolean filterApp(AppEntry info) {
+ return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
new file mode 100644
index 0000000..d34dd89
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.applications;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+public class InterestingConfigChanges {
+ private final Configuration mLastConfiguration = new Configuration();
+ private int mLastDensity;
+
+ public boolean applyNewConfig(Resources res) {
+ int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+ boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
+ if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
+ |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
+ mLastDensity = res.getDisplayMetrics().densityDpi;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c026d77..14c8262 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -911,7 +911,7 @@
<string name="monitoring_description_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your administrator.</string>
<!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
- <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites.</string>
+ <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
<!-- Monitoring dialog VPN with device owner text [CHAR LIMIT=400] -->
<string name="monitoring_description_vpn_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
@@ -923,16 +923,16 @@
<string name="legacy_vpn_name">VPN</string>
<!-- Monitoring dialog text for single app (no profile or device owner) [CHAR LIMIT=400] -->
- <string name="monitoring_description_app">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites.</string>
+ <string name="monitoring_description_app">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your network activity including emails, apps, and websites.</string>
<!-- Monitoring dialog text for single app (inside personal profile) [CHAR LIMIT=400] -->
- <string name="monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites.</string>
+ <string name="monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps, and websites.</string>
<!-- Monitoring dialog text for single app (inside work profile) [CHAR LIMIT=400] -->
- <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator.</string>
+ <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
<!-- Monitoring dialog text for multiple apps (in personal and work profiles) [CHAR LIMIT=400] -->
- <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
+ <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
<!-- Monitoring dialog text for single app (with device owner) [CHAR LIMIT=400] -->
<string name="monitoring_description_vpn_app_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
@@ -1006,7 +1006,7 @@
<string name="volumeui_notification_text">Touch to restore the original.</string>
<!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
- <string name="managed_profile_foreground_toast">You are in the Work profile</string>
+ <string name="managed_profile_foreground_toast">You are in the work profile</string>
<string-array name="volume_stream_titles" translatable="false">
<item>Voice calls</item> <!-- STREAM_VOICE_CALL -->
@@ -1087,4 +1087,10 @@
<!-- Alarm template for far alarms [CHAR LIMIT=25] -->
<string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>
+ <!-- Accessibility label for Quick Settings detail screens [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_detail">Quick Settings, <xliff:g id="title" example="Wi-Fi">%s</xliff:g>.</string>
+
+ <!-- Accessibility label for hotspot icon [CHAR LIMIT=NONE] -->
+ <string name="accessibility_status_bar_hotspot">Hotspot</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 79af706..f129288 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -124,6 +124,14 @@
startAssist();
}
+ public void hideAssist() {
+ try {
+ mVoiceInteractionManagerService.hideCurrentSession();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call hideCurrentSession", e);
+ }
+ }
+
private WindowManager.LayoutParams getLayoutParams() {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 1f3a830..3f72125 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -82,11 +82,9 @@
sNotificationPulseStats.append();
}
- public static void traceDozing(Context context, boolean dozing) {
- if (!ENABLED) return;
- sPulsing = false;
+ private static void init(Context context) {
synchronized (DozeLog.class) {
- if (dozing && sMessages == null) {
+ if (sMessages == null) {
sTimes = new long[SIZE];
sMessages = new String[SIZE];
sSince = System.currentTimeMillis();
@@ -105,6 +103,12 @@
KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
}
}
+ }
+
+ public static void traceDozing(Context context, boolean dozing) {
+ if (!ENABLED) return;
+ sPulsing = false;
+ init(context);
log("dozing " + dozing);
}
@@ -146,10 +150,12 @@
}
}
- public static void traceProximityResult(boolean near, long millis, int pulseReason) {
+ public static void traceProximityResult(Context context, boolean near, long millis,
+ int pulseReason) {
if (!ENABLED) return;
log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
+ " millis=" + millis);
+ init(context);
sProxStats[pulseReason][near ? 0 : 1].append();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 8d27450..5d46712 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -235,7 +235,7 @@
public void onProximityResult(int result) {
final boolean isNear = result == RESULT_NEAR;
final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(isNear, end - start, reason);
+ DozeLog.traceProximityResult(mContext, isNear, end - start, reason);
if (nonBlocking) {
// we already continued
return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 86910fe..f00fed5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -455,7 +455,7 @@
@Override
public void onFingerprintAuthenticated(int userId) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mViewMediatorCallback.keyguardDone(true);
+ mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
} else {
mStatusBarKeyguardViewManager.animateCollapsePanels(
FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 88d0997..2a84362 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -96,6 +96,7 @@
.create();
mDialog.create();
+ mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this);
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index cd4f299..25e3d10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -111,6 +111,8 @@
mDetailDoneButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
+ announceForAccessibility(
+ mContext.getString(R.string.accessibility_desc_quick_settings));
closeDetail();
}
});
@@ -392,6 +394,9 @@
mDetail.bringToFront();
mDetailContent.addView(r.detailView);
MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
+ announceForAccessibility(mContext.getString(
+ R.string.accessibility_quick_settings_detail,
+ mContext.getString(detailAdapter.getTitle())));
setDetailRecord(r);
listener = mHideGridContentWhenDone;
if (r instanceof TileRecord) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6fb4b48..c945fc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -92,6 +92,7 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -240,6 +241,8 @@
private NotificationClicker mNotificationClicker = new NotificationClicker();
+ protected AssistManager mAssistManager;
+
@Override // NotificationData.Environment
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
@@ -1626,6 +1629,7 @@
// TODO: Dismiss Keyguard.
}
if (intent.isActivity()) {
+ mAssistManager.hideAssist();
overrideActivityPendingAppTransition(keyguardShowing
&& !afterKeyguardGone);
}
@@ -1644,7 +1648,7 @@
true /* force */, true /* delayed */);
visibilityChanged(false);
- return intent != null && intent.isActivity();
+ return true;
}
}, afterKeyguardGone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
index 00665f4..a323684 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
@@ -48,15 +48,14 @@
public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mAnimatedDismissDrawable = (AnimatedVectorDrawable) getContext().getResources().getDrawable(
+ mAnimatedDismissDrawable = (AnimatedVectorDrawable) getContext().getDrawable(
R.drawable.dismiss_all_shape_animation).mutate();
mAnimatedDismissDrawable.setCallback(this);
mAnimatedDismissDrawable.setBounds(0,
0,
mAnimatedDismissDrawable.getIntrinsicWidth(),
mAnimatedDismissDrawable.getIntrinsicHeight());
- mStaticDismissDrawable = getContext().getResources().getDrawable(
- R.drawable.dismiss_all_shape);
+ mStaticDismissDrawable = getContext().getDrawable(R.drawable.dismiss_all_shape);
mStaticDismissDrawable.setBounds(0,
0,
mStaticDismissDrawable.getIntrinsicWidth(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 9ef495d..d444ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -903,12 +903,18 @@
return mShowingPublic ? mPublicLayout : mPrivateLayout;
}
+ @Override
+ public void setShowingLegacyBackground(boolean showing) {
+ super.setShowingLegacyBackground(showing);
+ mPrivateLayout.setShowingLegacyBackground(showing);
+ mPublicLayout.setShowingLegacyBackground(showing);
+ }
+
public void setExpansionLogger(ExpansionLogger logger, String key) {
mLogger = logger;
mLoggingKey = key;
}
-
private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
final boolean nowExpanded = isExpanded();
if (wasExpanded != nowExpanded && mLogger != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index dec2fc7..8172a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -67,6 +67,8 @@
private final Paint mFadePaint = new Paint();
private boolean mAnimate;
private boolean mIsHeadsUp;
+ private boolean mShowingLegacyBackground;
+
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -421,7 +423,7 @@
public void setDark(boolean dark, boolean fade, long delay) {
if (mDark == dark || mContractedChild == null) return;
mDark = dark;
- mContractedWrapper.setDark(dark, fade, delay);
+ mContractedWrapper.setDark(dark && !mShowingLegacyBackground, fade, delay);
}
public void setHeadsUp(boolean headsUp) {
@@ -436,4 +438,8 @@
// layout, and saves us some layers.
return false;
}
+
+ public void setShowingLegacyBackground(boolean showing) {
+ mShowingLegacyBackground = showing;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 3d57d54..3737d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -228,4 +228,9 @@
ensureView();
return mKeyguardView.interceptMediaKey(event);
}
+
+ public void notifyKeyguardAuthenticated() {
+ ensureView();
+ mKeyguardView.finish();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 39dc480..9e1af82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -36,6 +36,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -205,6 +206,10 @@
}
};
+ /** Interpolator to be used for animations that respond directly to a touch */
+ private final Interpolator mTouchResponseInterpolator =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(!DEBUG);
@@ -939,7 +944,7 @@
mQsExpansionFromOverscroll = false;
updateQsState();
}
- });
+ }, false /* isClick */);
}
private void onQsExpansionStarted() {
@@ -1390,10 +1395,11 @@
}
private void flingSettings(float vel, boolean expand) {
- flingSettings(vel, expand, null);
+ flingSettings(vel, expand, null, false /* isClick */);
}
- private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
+ private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable,
+ boolean isClick) {
float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
if (target == mQsExpansionHeight) {
mScrollYOverride = -1;
@@ -1408,7 +1414,12 @@
}
mScrollView.setBlockFlinging(true);
ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
- mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
+ if (isClick) {
+ animator.setInterpolator(mTouchResponseInterpolator);
+ animator.setDuration(368);
+ } else {
+ mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
+ }
if (belowFalsingThreshold) {
animator.setDuration(350);
}
@@ -1870,12 +1881,12 @@
if (v == mHeader) {
onQsExpansionStarted();
if (mQsExpanded) {
- flingSettings(0 /* vel */, false /* expand */);
+ flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
} else if (mQsExpansionEnabled) {
EventLogTags.writeSysuiLockscreenGesture(
EventLogConstants.SYSUI_TAP_TO_OPEN_QS,
0, 0);
- flingSettings(0 /* vel */, true /* expand */);
+ flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
}
}
}
@@ -2112,7 +2123,9 @@
public void setDozing(boolean dozing, boolean animate) {
if (dozing == mDozing) return;
mDozing = dozing;
- updateDozingVisibilities(animate);
+ if (mStatusBarState == StatusBarState.KEYGUARD) {
+ updateDozingVisibilities(animate);
+ }
}
private void updateDozingVisibilities(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f1e51a52..569b918 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -32,7 +32,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -345,8 +344,6 @@
private int mNavigationIconHints = 0;
private HandlerThread mHandlerThread;
- private AssistManager mAssistManager;
-
// ensure quick settings is disabled until the current user makes it through the setup wizard
private boolean mUserSetup = false;
private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
@@ -412,6 +409,7 @@
private boolean mWaitingForKeyguardExit;
private boolean mDozing;
+ private boolean mDozingRequested;
private boolean mScrimSrcModeEnabled;
private Interpolator mLinearInterpolator = new LinearInterpolator();
@@ -2753,6 +2751,7 @@
final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
Runnable runnable = new Runnable() {
public void run() {
+ mAssistManager.hideAssist();
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int result = ActivityManager.START_CANCELED;
@@ -2776,10 +2775,13 @@
Runnable cancelRunnable = new Runnable() {
@Override
public void run() {
- callback.onActivityStarted(ActivityManager.START_CANCELED);
+ if (callback != null) {
+ callback.onActivityStarted(ActivityManager.START_CANCELED);
+ }
}
};
- executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade, afterKeyguardGone);
+ executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
+ afterKeyguardGone);
}
public void executeRunnableDismissingKeyguard(final Runnable runnable,
@@ -3478,9 +3480,6 @@
}
private void updateDozingState() {
- if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
- return;
- }
boolean animate = !mDozing && mDozeScrimController.isPulsing();
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate, mScreenOnTouchLocation);
@@ -3597,6 +3596,7 @@
mState = state;
mGroupManager.setStatusBarState(state);
mStatusBarWindowManager.setStatusBarState(state);
+ updateDozing();
}
@Override
@@ -3912,6 +3912,11 @@
}
}
+ private void updateDozing() {
+ mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD;
+ updateDozingState();
+ }
+
private final class ShadeUpdates {
private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
@@ -4017,10 +4022,10 @@
}
private void handleStartDozing(@NonNull Runnable ready) {
- if (!mDozing) {
- mDozing = true;
+ if (!mDozingRequested) {
+ mDozingRequested = true;
DozeLog.traceDozing(mContext, mDozing);
- updateDozingState();
+ updateDozing();
}
ready.run();
}
@@ -4030,10 +4035,10 @@
}
private void handleStopDozing() {
- if (mDozing) {
- mDozing = false;
+ if (mDozingRequested) {
+ mDozingRequested = false;
DozeLog.traceDozing(mContext, mDozing);
- updateDozingState();
+ updateDozing();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0872e06..6a6266e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -162,7 +162,8 @@
mCast.addCallback(mCastCallback);
// hotspot
- mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0, null);
+ mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0,
+ mContext.getString(R.string.accessibility_status_bar_hotspot));
mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
mHotspot.addCallback(mHotspotCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 181926c..dfc6924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -110,7 +110,6 @@
private NextAlarmController mNextAlarmController;
private QSPanel mQSPanel;
-
private final Rect mClipBounds = new Rect();
private boolean mCaptureValues;
@@ -121,6 +120,7 @@
private float mCurrentT;
private boolean mShowingDetail;
+ private boolean mDetailTransitioning;
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -623,7 +623,7 @@
mSettingsButton.setTranslationX(values.settingsTranslation);
mSettingsButton.setRotation(values.settingsRotation);
applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
- if (!mShowingDetail) {
+ if (!mShowingDetail && !mDetailTransitioning) {
// Otherwise it needs to stay invisible
applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
}
@@ -706,6 +706,7 @@
@Override
public void onShowingDetail(final QSTile.DetailAdapter detail) {
+ mDetailTransitioning = true;
post(new Runnable() {
@Override
public void run() {
@@ -788,6 +789,7 @@
if (!in) {
v.setVisibility(INVISIBLE);
}
+ mDetailTransitioning = false;
}
})
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6cb890a..fcf3a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -447,4 +447,12 @@
mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
false /* delayed */, speedUpFactor);
}
+
+ /**
+ * Notifies that the user has authenticated by other means than using the bouncer, for example,
+ * fingerprint.
+ */
+ public void notifyKeyguardAuthenticated() {
+ mBouncer.notifyKeyguardAuthenticated();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index eac5e79..c31244c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -96,6 +96,7 @@
private ExpandableNotificationRow mChildExpandingView;
private int mHeadsUpAppearHeightBottom;
private boolean mShadeExpanded;
+ private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
@@ -794,6 +795,10 @@
private void onAnimationFinished() {
mHostLayout.onChildAnimationFinished();
+ for (View v : mChildrenToClearFromOverlay) {
+ mHostLayout.getOverlay().remove(v);
+ }
+ mChildrenToClearFromOverlay.clear();
}
/**
@@ -880,8 +885,20 @@
finalState.applyState(changingView, mTmpState);
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
- // This item is added, initialize it's properties.
mHeadsUpDisappearChildren.add(changingView);
+ if (mHostLayout.indexOfChild(changingView) == -1) {
+ // This notification was actually removed, so we need to add it to the overlay
+ mHostLayout.getOverlay().add(changingView);
+ ViewState viewState = new ViewState();
+ viewState.initFrom(changingView);
+ viewState.yTranslation = -changingView.getActualHeight();
+ // We temporarily enable Y animations, the real filter will be combined
+ // afterwards anyway
+ mAnimationFilter.animateY = true;
+ startViewAnimations(changingView, viewState, 0,
+ ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
+ mChildrenToClearFromOverlay.add(changingView);
+ }
}
mNewEvents.add(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 310a64c..29bea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -386,18 +386,8 @@
}
}
} else {
- if (mAutomute && !row.ss.muteSupported) {
- final boolean vmute = row.ss.level == 0;
- mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
- } else {
- final boolean mute = !row.ss.muted;
- mController.setStreamMute(stream, mute);
- if (mAutomute) {
- if (!mute && row.ss.level == 0) {
- mController.setStreamVolume(stream, 1);
- }
- }
- }
+ final boolean vmute = row.ss.level == 0;
+ mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
}
row.userAttempt = 0; // reset the grace period, slider should update immediately
}
@@ -589,6 +579,9 @@
if (ss.level > 0) {
row.lastAudibleLevel = ss.level;
}
+ if (ss.level == row.requestedLevel) {
+ row.requestedLevel = -1;
+ }
final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
@@ -664,7 +657,10 @@
row.icon.setContentDescription(ss.name);
// update slider
- updateVolumeRowSliderH(row, zenMuted);
+ final boolean enableSlider = !zenMuted;
+ final int vlevel = row.ss.muted && (isRingVibrate || !isRingStream && !zenMuted) ? 0
+ : row.ss.level;
+ updateVolumeRowSliderH(row, enableSlider, vlevel);
}
private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
@@ -676,8 +672,8 @@
row.slider.setThumbTintList(tint);
}
- private void updateVolumeRowSliderH(VolumeRow row, boolean zenMuted) {
- row.slider.setEnabled(!zenMuted);
+ private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel) {
+ row.slider.setEnabled(enable);
updateVolumeRowSliderTintH(row, row.stream == mActiveStream);
if (row.tracking) {
return; // don't update if user is sliding
@@ -694,7 +690,6 @@
row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
return; // don't update if visible and in grace period
}
- final int vlevel = row.ss.muted ? 0 : row.ss.level;
if (vlevel == level) {
if (mShowing && rowVisible) {
return; // don't clamp if visible
@@ -1018,7 +1013,7 @@
private StreamState ss;
private long userAttempt; // last user-driven slider change
private boolean tracking; // tracking slider touch
- private int requestedLevel;
+ private int requestedLevel = -1; // pending user-requested level via progress changed
private int iconRes;
private int iconMuteRes;
private boolean important;
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
new file mode 100644
index 0000000..6390bcd2
--- /dev/null
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.lang.Float;
+
+/**
+ * Determines if the device has been set upon a stationary object.
+ */
+public class AnyMotionDetector {
+ interface DeviceIdleCallback {
+ public void onAnyMotionResult(int result);
+ }
+
+ private static final String TAG = "AnyMotionDetector";
+
+ private static final boolean DEBUG = false;
+
+ /** Stationary status is unknown due to insufficient orientation measurements. */
+ public static final int RESULT_UNKNOWN = -1;
+
+ /** Device is stationary, e.g. still on a table. */
+ public static final int RESULT_STATIONARY = 0;
+
+ /** Device has been moved. */
+ public static final int RESULT_MOVED = 1;
+
+ /** Orientation measurements are being performed or are planned. */
+ private static final int STATE_INACTIVE = 0;
+
+ /** No orientation measurements are being performed or are planned. */
+ private static final int STATE_ACTIVE = 1;
+
+ /** Current measurement state. */
+ private int mState;
+
+ /** Threshold angle in degrees beyond which the device is considered moving. */
+ private final float THRESHOLD_ANGLE = 2f;
+
+ /** Threshold energy above which the device is considered moving. */
+ private final float THRESHOLD_ENERGY = 5f;
+
+ /** The duration of the accelerometer orientation measurement. */
+ private static final long ORIENTATION_MEASUREMENT_DURATION_MILLIS = 2500;
+
+ /** The maximum duration we will collect accelerometer data. */
+ private static final long ACCELEROMETER_DATA_TIMEOUT_MILLIS = 3000;
+
+ /** The interval between accelerometer orientation measurements. */
+ private static final long ORIENTATION_MEASUREMENT_INTERVAL_MILLIS = 5000;
+
+ /**
+ * The duration in milliseconds after which an orientation measurement is considered
+ * too stale to be used.
+ */
+ private static final int STALE_MEASUREMENT_TIMEOUT_MILLIS = 2 * 60 * 1000;
+
+ /** The accelerometer sampling interval. */
+ private static final int SAMPLING_INTERVAL_MILLIS = 40;
+
+ private AlarmManager mAlarmManager;
+ private final Handler mHandler;
+ private Intent mAlarmIntent;
+ private final Object mLock = new Object();
+ private Sensor mAccelSensor;
+ private SensorManager mSensorManager;
+ private PowerManager.WakeLock mWakeLock;
+
+ /** The time when detection was last performed. */
+ private long mDetectionStartTime;
+
+ /** The minimum number of samples required to detect AnyMotion. */
+ private int mNumSufficientSamples;
+
+ /** True if an orientation measurement is in progress. */
+ private boolean mMeasurementInProgress;
+
+ /** The most recent gravity vector. */
+ private Vector3 mCurrentGravityVector = null;
+
+ /** The second most recent gravity vector. */
+ private Vector3 mPreviousGravityVector = null;
+
+ /** Running sum of squared errors. */
+ private RunningSignalStats mRunningStats;
+
+ private DeviceIdleCallback mCallback = null;
+
+ public AnyMotionDetector(AlarmManager am, PowerManager pm, Handler handler, SensorManager sm,
+ DeviceIdleCallback callback) {
+ if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
+ mAlarmManager = am;
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mHandler = handler;
+ mSensorManager = sm;
+ mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mMeasurementInProgress = false;
+ mState = STATE_INACTIVE;
+ mCallback = callback;
+ mRunningStats = new RunningSignalStats();
+ mNumSufficientSamples = (int) Math.ceil(
+ ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
+ if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+ }
+
+ /*
+ * Acquire accel data until we determine AnyMotion status.
+ */
+ public void checkForAnyMotion() {
+ if (DEBUG) Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+ if (mState != STATE_ACTIVE) {
+ mState = STATE_ACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
+ mCurrentGravityVector = null;
+ mPreviousGravityVector = null;
+ startOrientationMeasurement();
+ }
+ }
+
+ private void startOrientationMeasurement() {
+ if (DEBUG) Slog.d(TAG, "startOrientationMeasurement: mMeasurementInProgress=" +
+ mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
+
+ if (!mMeasurementInProgress && mAccelSensor != null) {
+ if (mSensorManager.registerListener(mListener, mAccelSensor,
+ SAMPLING_INTERVAL_MILLIS * 1000)) {
+ mWakeLock.acquire();
+ mMeasurementInProgress = true;
+ mDetectionStartTime = SystemClock.elapsedRealtime();
+ mRunningStats.reset();
+ }
+
+ Message msg = Message.obtain(mHandler, mMeasurementTimeout);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+ }
+ }
+
+ private int stopOrientationMeasurementLocked() {
+ if (DEBUG) Slog.d(TAG, "stopOrientationMeasurement. mMeasurementInProgress=" +
+ mMeasurementInProgress);
+ int status = RESULT_UNKNOWN;
+ if (mMeasurementInProgress) {
+ mSensorManager.unregisterListener(mListener);
+ mHandler.removeCallbacks(mMeasurementTimeout);
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ long detectionEndTime = SystemClock.elapsedRealtime();
+ mMeasurementInProgress = false;
+ mPreviousGravityVector = mCurrentGravityVector;
+ mCurrentGravityVector = mRunningStats.getRunningAverage();
+ if (DEBUG) {
+ Slog.d(TAG, "mRunningStats = " + mRunningStats.toString());
+ String currentGravityVectorString = (mCurrentGravityVector == null) ?
+ "null" : mCurrentGravityVector.toString();
+ String previousGravityVectorString = (mPreviousGravityVector == null) ?
+ "null" : mPreviousGravityVector.toString();
+ Slog.d(TAG, "mCurrentGravityVector = " + currentGravityVectorString);
+ Slog.d(TAG, "mPreviousGravityVector = " + previousGravityVectorString);
+ }
+ mRunningStats.reset();
+ status = getStationaryStatus();
+ if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
+ if (status != RESULT_UNKNOWN) {
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " +
+ status);
+ mState = STATE_INACTIVE;
+ } else {
+ /*
+ * Unknown due to insufficient measurements. Schedule another orientation
+ * measurement.
+ */
+ if (DEBUG) Slog.d(TAG, "stopOrientationMeasurementLocked(): another measurement" +
+ " scheduled in " + ORIENTATION_MEASUREMENT_INTERVAL_MILLIS +
+ " milliseconds.");
+ Message msg = Message.obtain(mHandler, mSensorRestart);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
+ }
+ }
+ return status;
+ }
+
+ /*
+ * Updates mStatus to the current AnyMotion status.
+ */
+ public int getStationaryStatus() {
+ if ((mPreviousGravityVector == null) || (mCurrentGravityVector == null)) {
+ return RESULT_UNKNOWN;
+ }
+ Vector3 previousGravityVectorNormalized = mPreviousGravityVector.normalized();
+ Vector3 currentGravityVectorNormalized = mCurrentGravityVector.normalized();
+ float angle = previousGravityVectorNormalized.angleBetween(currentGravityVectorNormalized);
+ if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle);
+ if ((angle < THRESHOLD_ANGLE) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
+ return RESULT_STATIONARY;
+ } else if (Float.isNaN(angle)) {
+ /**
+ * Floating point rounding errors have caused the angle calcuation's dot product to
+ * exceed 1.0. In such case, we report RESULT_MOVED to prevent devices from rapidly
+ * retrying this measurement.
+ */
+ return RESULT_MOVED;
+ }
+ long diffTime = mCurrentGravityVector.timeMillisSinceBoot -
+ mPreviousGravityVector.timeMillisSinceBoot;
+ if (diffTime > STALE_MEASUREMENT_TIMEOUT_MILLIS) {
+ if (DEBUG) Slog.d(TAG, "getStationaryStatus: mPreviousGravityVector is too stale at " +
+ diffTime + " ms ago. Returning RESULT_UNKNOWN.");
+ return RESULT_UNKNOWN;
+ }
+ return RESULT_MOVED;
+ }
+
+ private final SensorEventListener mListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ int status = RESULT_UNKNOWN;
+ synchronized (mLock) {
+ Vector3 accelDatum = new Vector3(SystemClock.elapsedRealtime(), event.values[0],
+ event.values[1], event.values[2]);
+ mRunningStats.accumulate(accelDatum);
+
+ // If we have enough samples, stop accelerometer data acquisition.
+ if (mRunningStats.getSampleCount() >= mNumSufficientSamples) {
+ status = stopOrientationMeasurementLocked();
+ }
+ }
+ if (status != RESULT_UNKNOWN) {
+ mCallback.onAnyMotionResult(status);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ private final Runnable mSensorRestart = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ startOrientationMeasurement();
+ }
+ }
+ };
+
+ private final Runnable mMeasurementTimeout = new Runnable() {
+ @Override
+ public void run() {
+ int status = RESULT_UNKNOWN;
+ synchronized (mLock) {
+ if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+ "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
+ "orientation measurement.");
+ status = stopOrientationMeasurementLocked();
+ }
+ if (status != RESULT_UNKNOWN) {
+ mCallback.onAnyMotionResult(status);
+ }
+ }
+ };
+
+ /**
+ * A timestamped three dimensional vector and some vector operations.
+ */
+ private static class Vector3 {
+ public long timeMillisSinceBoot;
+ public float x;
+ public float y;
+ public float z;
+
+ public Vector3(long timeMillisSinceBoot, float x, float y, float z) {
+ this.timeMillisSinceBoot = timeMillisSinceBoot;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ private float norm() {
+ return (float) Math.sqrt(dotProduct(this));
+ }
+
+ private Vector3 normalized() {
+ float mag = norm();
+ return new Vector3(timeMillisSinceBoot, x / mag, y / mag, z / mag);
+ }
+
+ /**
+ * Returns the angle between this 3D vector and another given 3D vector.
+ * Assumes both have already been normalized.
+ *
+ * @param other The other Vector3 vector.
+ * @return angle between this vector and the other given one.
+ */
+ public float angleBetween(Vector3 other) {
+ double degrees = Math.toDegrees(Math.acos(this.dotProduct(other)));
+ float returnValue = (float) degrees;
+ Slog.d(TAG, "angleBetween: this = " + this.toString() +
+ ", other = " + other.toString());
+ Slog.d(TAG, " degrees = " + degrees + ", returnValue = " + returnValue);
+ return returnValue;
+ }
+
+ @Override
+ public String toString() {
+ String msg = "";
+ msg += "timeMillisSinceBoot=" + timeMillisSinceBoot;
+ msg += " | x=" + x;
+ msg += ", y=" + y;
+ msg += ", z=" + z;
+ return msg;
+ }
+
+ public float dotProduct(Vector3 v) {
+ return x * v.x + y * v.y + z * v.z;
+ }
+
+ public Vector3 times(float val) {
+ return new Vector3(timeMillisSinceBoot, x * val, y * val, z * val);
+ }
+
+ public Vector3 plus(Vector3 v) {
+ return new Vector3(v.timeMillisSinceBoot, x + v.x, y + v.y, z + v.z);
+ }
+
+ public Vector3 minus(Vector3 v) {
+ return new Vector3(v.timeMillisSinceBoot, x - v.x, y - v.y, z - v.z);
+ }
+ }
+
+ /**
+ * Maintains running statistics on the signal revelant to AnyMotion detection, including:
+ * <ul>
+ * <li>running average.
+ * <li>running sum-of-squared-errors as the energy of the signal derivative.
+ * <ul>
+ */
+ private static class RunningSignalStats {
+ Vector3 previousVector;
+ Vector3 currentVector;
+ Vector3 runningSum;
+ float energy;
+ int sampleCount;
+
+ public RunningSignalStats() {
+ reset();
+ }
+
+ public void reset() {
+ previousVector = null;
+ currentVector = null;
+ runningSum = new Vector3(0, 0, 0, 0);
+ energy = 0;
+ sampleCount = 0;
+ }
+
+ /**
+ * Apply a 3D vector v as the next element in the running SSE.
+ */
+ public void accumulate(Vector3 v) {
+ if (v == null) {
+ if (DEBUG) Slog.i(TAG, "Cannot accumulate a null vector.");
+ return;
+ }
+ sampleCount++;
+ runningSum = runningSum.plus(v);
+ previousVector = currentVector;
+ currentVector = v;
+ if (previousVector != null) {
+ Vector3 dv = currentVector.minus(previousVector);
+ float incrementalEnergy = dv.x * dv.x + dv.y * dv.y + dv.z * dv.z;
+ energy += incrementalEnergy;
+ if (DEBUG) Slog.i(TAG, "Accumulated vector " + currentVector.toString() +
+ ", runningSum = " + runningSum.toString() +
+ ", incrementalEnergy = " + incrementalEnergy +
+ ", energy = " + energy);
+ }
+ }
+
+ public Vector3 getRunningAverage() {
+ if (sampleCount > 0) {
+ return runningSum.times((float)(1.0f / sampleCount));
+ }
+ return null;
+ }
+
+ public float getEnergy() {
+ return energy;
+ }
+
+ public int getSampleCount() {
+ return sampleCount;
+ }
+
+ @Override
+ public String toString() {
+ String msg = "";
+ String currentVectorString = (currentVector == null) ?
+ "null" : currentVector.toString();
+ String previousVectorString = (previousVector == null) ?
+ "null" : previousVector.toString();
+ msg += "previousVector = " + previousVectorString;
+ msg += ", currentVector = " + currentVectorString;
+ msg += ", sampleCount = " + sampleCount;
+ msg += ", energy = " + energy;
+ return msg;
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index e9759c3..4c7b523 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -85,10 +85,12 @@
/**
* Keeps track of device idleness and drives low power mode based on that.
*/
-public class DeviceIdleController extends SystemService {
+public class DeviceIdleController extends SystemService
+ implements AnyMotionDetector.DeviceIdleCallback {
private static final String TAG = "DeviceIdleController";
private static final boolean DEBUG = false;
+
private static final boolean COMPRESS_TIME = false;
public static final String SERVICE_NAME = "deviceidle";
@@ -96,6 +98,9 @@
private static final String ACTION_STEP_IDLE_STATE =
"com.android.server.device_idle.STEP_IDLE_STATE";
+ private static final String ACTION_ENTER_INACTIVE_STATE =
+ "com.android.server.device_idle.ENTER_INACTIVE_STATE";
+
// TODO: These need to be moved to system settings.
/**
@@ -104,26 +109,40 @@
* immediately after going inactive just because we don't want to be continually running
* the significant motion sensor whenever the screen is off.
*/
+
private static final long DEFAULT_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L
: 3 * 60 * 1000L;
+
+ /**
+ * If we don't receive a callback from AnyMotion in this amount of time, we will change from
+ * STATE_SENSING to STATE_INACTIVE, and any AnyMotion callbacks while not in STATE_SENSING will
+ * be ignored.
+ */
+ private static final long DEFAULT_SENSING_TIMEOUT = !DEBUG ? 5 * 60 * 1000L : 60 * 1000L;
+
/**
* This is the time, after seeing motion, that we wait after becoming inactive from
* that until we start looking for motion again.
*/
private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 10*60*1000L
: 60 * 1000L;
+
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
* for significant motion until we truly consider the device to be idle.
*/
+
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L
: 3 * 60 * 1000L;
+
/**
* This is the initial time, after being idle, that we will allow ourself to be back
* in the IDLE_PENDING state allowing the system to run normally until we return to idle.
*/
+
private static final long DEFAULT_IDLE_PENDING_TIMEOUT = !COMPRESS_TIME ? 5*60*1000L
: 30 * 1000L;
+
/**
* Maximum pending idle timeout (time spent running) we will be allowed to use.
*/
@@ -138,8 +157,10 @@
* This is the initial time that we want to sit in the idle state before waking up
* again to return to pending idle and allowing normal work to run.
*/
+
private static final long DEFAULT_IDLE_TIMEOUT = !COMPRESS_TIME ? 60*60*1000L
: 6 * 60 * 1000L;
+
/**
* Maximum idle duration we will be allowed to use.
*/
@@ -168,9 +189,11 @@
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
private Sensor mSigMotionSensor;
+ private PendingIntent mSensingAlarmIntent;
private PendingIntent mAlarmIntent;
private Intent mIdleIntent;
private Display mCurDisplay;
+ private AnyMotionDetector mAnyMotionDetector;
private boolean mIdleDisabled;
private boolean mScreenOn;
private boolean mCharging;
@@ -182,15 +205,18 @@
private static final int STATE_INACTIVE = 1;
/** Device is past the initial inactive period, and waiting for the next idle period. */
private static final int STATE_IDLE_PENDING = 2;
+ /** Device is currently sensing motion. */
+ private static final int STATE_SENSING = 3;
/** Device is in the idle state, trying to stay asleep as much as possible. */
- private static final int STATE_IDLE = 3;
+ private static final int STATE_IDLE = 4;
/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
- private static final int STATE_IDLE_MAINTENANCE = 4;
+ private static final int STATE_IDLE_MAINTENANCE = 5;
private static String stateToString(int state) {
switch (state) {
case STATE_ACTIVE: return "ACTIVE";
case STATE_INACTIVE: return "INACTIVE";
case STATE_IDLE_PENDING: return "IDLE_PENDING";
+ case STATE_SENSING: return "SENSING";
case STATE_IDLE: return "IDLE";
case STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
default: return Integer.toString(state);
@@ -247,6 +273,10 @@
synchronized (DeviceIdleController.this) {
stepIdleStateLocked();
}
+ } else if (ACTION_ENTER_INACTIVE_STATE.equals(intent.getAction())) {
+ synchronized (DeviceIdleController.this) {
+ enterInactiveStateLocked();
+ }
}
}
};
@@ -276,6 +306,24 @@
}
};
+ @Override
+ public void onAnyMotionResult(int result) {
+ if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
+ if (mState == STATE_SENSING) {
+ if (result == AnyMotionDetector.RESULT_STATIONARY) {
+ if (DEBUG) Slog.d(TAG, "RESULT_STATIONARY received.");
+ synchronized (this) {
+ stepIdleStateLocked();
+ }
+ } else if (result == AnyMotionDetector.RESULT_MOVED) {
+ if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
+ synchronized (this) {
+ enterInactiveStateLocked();
+ }
+ }
+ }
+ }
+
static final int MSG_WRITE_CONFIG = 1;
static final int MSG_REPORT_IDLE_ON = 2;
static final int MSG_REPORT_IDLE_OFF = 3;
@@ -288,6 +336,7 @@
}
@Override public void handleMessage(Message msg) {
+ if (DEBUG) Slog.d(TAG, "handleMessage(" + msg.what + ")");
switch (msg.what) {
case MSG_WRITE_CONFIG: {
handleWriteConfigFile();
@@ -452,12 +501,21 @@
Context.DISPLAY_SERVICE);
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+ mAnyMotionDetector = new AnyMotionDetector(
+ mAlarmManager,
+ (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
+ mHandler, mSensorManager, this);
Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
.setPackage("android")
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE)
+ .setPackage("android")
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0);
+
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -613,6 +671,7 @@
// because if there is anything shown we are going to be updating it at some
// frequency so can't be allowed to go into deep sleeps.
boolean screenOn = mCurDisplay.getState() != Display.STATE_OFF;;
+ if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);
if (!screenOn && mScreenOn) {
mScreenOn = false;
becomeInactiveIfAppropriateLocked();
@@ -623,6 +682,7 @@
}
void updateChargingLocked(boolean charging) {
+ if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
if (!charging && mCharging) {
mCharging = false;
becomeInactiveIfAppropriateLocked();
@@ -639,6 +699,7 @@
}
void becomeActiveLocked(String reason) {
+ if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + reason);
if (mState != STATE_ACTIVE) {
EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
scheduleReportActiveLocked(false);
@@ -652,10 +713,12 @@
}
void becomeInactiveIfAppropriateLocked() {
+ if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
if (!mScreenOn && !mCharging && !mIdleDisabled && mState == STATE_ACTIVE) {
// Screen has turned off; we are now going to become inactive and start
// waiting to see if we will ultimately go idle.
mState = STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
scheduleAlarmLocked(mInactiveTimeout, false);
@@ -663,7 +726,17 @@
}
}
+ /**
+ * This is called when we've failed to receive a callback from AnyMotionDetector
+ * within the DEFAULT_SENSING_TIMEOUT, to return to STATE_INACTIVE.
+ */
+ void enterInactiveStateLocked() {
+ mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
+ becomeInactiveIfAppropriateLocked();
+ }
+
void stepIdleStateLocked() {
+ if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
final long now = SystemClock.elapsedRealtime();
@@ -685,26 +758,34 @@
mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
mState = STATE_IDLE_PENDING;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
EventLogTags.writeDeviceIdle(mState, "step");
break;
case STATE_IDLE_PENDING:
+ mState = STATE_SENSING;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
+ scheduleSensingAlarmLocked(DEFAULT_SENSING_TIMEOUT);
+ mAnyMotionDetector.checkForAnyMotion();
+ break;
+ case STATE_SENSING:
+ cancelSensingAlarmLocked();
case STATE_IDLE_MAINTENANCE:
- // We have been waiting to become idle, and now it is time! This is the
- // only case where we want to use a wakeup alarm, because we do want to
- // drag the device out of its sleep state in this case to do the next
- // scheduled work.
scheduleAlarmLocked(mNextIdleDelay, true);
- mNextIdleDelay = (long)(mNextIdleDelay*DEFAULT_IDLE_FACTOR);
+ if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
+ " ms.");
+ mNextIdleDelay = (long)(mNextIdleDelay * DEFAULT_IDLE_FACTOR);
+ if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
if (mNextIdleDelay > DEFAULT_MAX_IDLE_TIMEOUT) {
mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
}
mState = STATE_IDLE;
- EventLogTags.writeDeviceIdle(mState, "step");
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
break;
case STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
scheduleAlarmLocked(mNextIdlePendingDelay, false);
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
+ "Next alarm in " + mNextIdlePendingDelay + " ms.");
mNextIdlePendingDelay = (long)(mNextIdlePendingDelay*DEFAULT_IDLE_PENDING_FACTOR);
if (mNextIdlePendingDelay > DEFAULT_MAX_IDLE_PENDING_TIMEOUT) {
mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
@@ -717,6 +798,7 @@
}
void significantMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
// When the sensor goes off, its trigger is automatically removed.
mSigMotionActive = false;
// The device is not yet active, so we want to go back to the pending idle
@@ -732,6 +814,7 @@
}
void startMonitoringSignificantMotion() {
+ if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
if (mSigMotionSensor != null && !mSigMotionActive) {
mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
mSigMotionActive = true;
@@ -739,6 +822,7 @@
}
void stopMonitoringSignificantMotion() {
+ if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
if (mSigMotionActive) {
mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
mSigMotionActive = false;
@@ -752,7 +836,13 @@
}
}
+ void cancelSensingAlarmLocked() {
+ if (DEBUG) Slog.d(TAG, "cancelSensingAlarmLocked()");
+ mAlarmManager.cancel(mSensingAlarmIntent);
+ }
+
void scheduleAlarmLocked(long delay, boolean idleUntil) {
+ if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
if (mSigMotionSensor == null) {
// If there is no significant motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
@@ -770,6 +860,13 @@
}
}
+ void scheduleSensingAlarmLocked(long delay) {
+ if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
+ mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextAlarmTime, mSensingAlarmIntent);
+ }
+
private void updateWhitelistAppIdsLocked() {
mPowerSaveWhitelistAppIds.clear();
for (int i=0; i<mPowerSaveWhitelistApps.size(); i++) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 8c8be4e..14f8a00 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1434,7 +1434,7 @@
final VolumeInfo vol = findVolumeById(volId);
try {
- mConnector.execute("volume", "format", vol.id);
+ mConnector.execute("volume", "format", vol.id, "auto");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index bc93268..a5b0e59 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -92,7 +92,7 @@
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
- int callerUid;
+ int callerUserId;
int events;
@@ -100,6 +100,8 @@
int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+ boolean canReadPhoneState;
+
boolean matchPhoneStateListenerEvent(int events) {
return (callback != null) && ((events & this.events) != 0);
}
@@ -114,8 +116,9 @@
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
- + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
- + " events=" + Integer.toHexString(events) + "}";
+ + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+ + " events=" + Integer.toHexString(events)
+ + " canReadPhoneState=" + canReadPhoneState + "}";
}
}
@@ -190,13 +193,15 @@
private PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
- static final int PHONE_STATE_PERMISSION_MASK =
+ static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
+ PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
+ PhoneStateListener.LISTEN_VOLTE_STATE;
+
+ static final int CHECK_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
- PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
- PhoneStateListener.LISTEN_VOLTE_STATE;;
+ PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -348,11 +353,10 @@
@Override
public void addOnSubscriptionsChangedListener(String callingPackage,
IOnSubscriptionsChangedListener callback) {
- int callerUid = UserHandle.getCallingUserId();
- int myUid = UserHandle.myUserId();
+ int callerUserId = UserHandle.getCallingUserId();
if (VDBG) {
- log("listen oscl: E pkg=" + callingPackage + " myUid=" + myUid
- + " callerUid=" + callerUid + " callback=" + callback
+ log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ + " callerUserId=" + callerUserId + " callback=" + callback
+ " callback.asBinder=" + callback.asBinder());
}
@@ -364,7 +368,7 @@
return;
}
- Record r = null;
+ Record r;
synchronized (mRecords) {
// register
@@ -385,8 +389,9 @@
r.onSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
- r.callerUid = callerUid;
+ r.callerUserId = callerUserId;
r.events = 0;
+ r.canReadPhoneState = true; // permission has been enforced above
if (DBG) {
log("listen oscl: Register r=" + r);
}
@@ -454,19 +459,18 @@
private void listen(String callingPackage, IPhoneStateListener callback, int events,
boolean notifyNow, int subId) {
- int callerUid = UserHandle.getCallingUserId();
- int myUid = UserHandle.myUserId();
+ int callerUserId = UserHandle.getCallingUserId();
if (VDBG) {
log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
- + " notifyNow=" + notifyNow + " subId=" + subId + " myUid=" + myUid
- + " callerUid=" + callerUid);
+ + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
+ + UserHandle.myUserId() + " callerUserId=" + callerUserId);
}
if (events != PhoneStateListener.LISTEN_NONE) {
/* Checks permission and throws Security exception */
checkListenerPermission(events);
- if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
+ if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
@@ -475,7 +479,7 @@
synchronized (mRecords) {
// register
- Record r = null;
+ Record r;
find_and_add: {
IBinder b = callback.asBinder();
final int N = mRecords.size();
@@ -493,7 +497,10 @@
r.callback = callback;
r.callingPackage = callingPackage;
- r.callerUid = callerUid;
+ r.callerUserId = callerUserId;
+ boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
+ | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
+ r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -558,7 +565,7 @@
if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
r.callback.onCallStateChanged(mCallState[phoneId],
- mCallIncomingNumber[phoneId]);
+ getCallIncomingNumber(r, phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -638,6 +645,22 @@
}
}
+ private boolean canReadPhoneState(String callingPackage) {
+ boolean canReadPhoneState = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
+ if (canReadPhoneState &&
+ mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ return canReadPhoneState;
+ }
+
+ private String getCallIncomingNumber(Record record, int phoneId) {
+ // Hide the number if record's process has no READ_PHONE_STATE permission
+ return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
+ }
+
private void remove(IBinder binder) {
synchronized (mRecords) {
final int recordCount = mRecords.size();
@@ -669,7 +692,8 @@
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
(r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
- r.callback.onCallStateChanged(state, incomingNumber);
+ String incomingNumberOrEmpty = r.canReadPhoneState ? incomingNumber : "";
+ r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -699,7 +723,8 @@
(r.subId == subId) &&
(r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
- r.callback.onCallStateChanged(state, incomingNumber);
+ String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId);
+ r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1538,7 +1563,7 @@
}
- if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
+ if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PHONE_STATE, null);
}
@@ -1572,10 +1597,10 @@
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
- valid = r.callerUid == foregroundUser && r.matchPhoneStateListenerEvent(events);
+ valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events);
if (DBG | DBG_LOC) {
log("validateEventsAndUserLocked: valid=" + valid
- + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+ + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+ " r.events=" + r.events + " events=" + events);
}
} finally {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1046b29..bedc729 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -468,12 +468,6 @@
*/
String mDeviceOwnerName;
- /**
- * Preferred activities to start on boot/user switch, as set by DevicePolicyManager. Indexed
- * by userId.
- */
- SparseArray<ComponentName> mPreferredSetupActivities = new SparseArray<>();
-
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -3435,20 +3429,13 @@
// Factory test.
ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
} else {
- ComponentName preferredComponent = mPreferredSetupActivities.get(userId);
- if (preferredComponent != null) {
- ai = AppGlobals.getPackageManager().getActivityInfo(
- preferredComponent, flags, userId);
- }
- if (ai == null) {
- ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
- if (info != null) {
- ai = info.activityInfo;
- }
+ if (info != null) {
+ ai = info.activityInfo;
}
}
} catch (RemoteException e) {
@@ -8919,22 +8906,6 @@
}
@Override
- public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "updatePreferredSetupActivity called from non-system process");
- }
- synchronized (this) {
- if (preferredActivity == null) {
- mPreferredSetupActivities.delete(userId);
- } else {
- mPreferredSetupActivities.put(userId, preferredActivity);
- }
- }
- }
-
- @Override
public void updateDeviceOwner(String packageName) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index fd36b7e..b0d5765 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -22,6 +22,7 @@
import android.app.IUserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -73,13 +74,6 @@
private ClientMonitor mRemoveClient = null;
private final AppOpsManager mAppOps;
- // Message types. Used internally to dispatch messages to the correct callback.
- // Must agree with the list in fingerprint.h
- private static final int FINGERPRINT_ERROR = -1;
- private static final int FINGERPRINT_ACQUIRED = 1;
- private static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
- private static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
- private static final int FINGERPRINT_AUTHENTICATED = 5;
private static final long MS_PER_SEC = 1000;
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
private static final int MAX_FAILED_ATTEMPTS = 5;
@@ -207,7 +201,6 @@
if (mEnrollClient != null) {
if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
if (remaining == 0) {
- ContentResolver res = mContext.getContentResolver();
addTemplateForUser(mEnrollClient, fingerId);
removeClient(mEnrollClient);
}
@@ -262,14 +255,14 @@
}
void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
- IFingerprintServiceReceiver receiver, int flags) {
+ IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll: no fingeprintd!");
return;
}
stopPendingOperations();
- mEnrollClient = new ClientMonitor(token, receiver, groupId);
+ mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted);
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.enroll(cryptoToken, groupId, timeout);
@@ -328,14 +321,14 @@
}
void startAuthentication(IBinder token, long opId, int groupId,
- IFingerprintServiceReceiver receiver, int flags) {
+ IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startAuthentication: no fingeprintd!");
return;
}
stopPendingOperations();
- mAuthClient = new ClientMonitor(token, receiver, groupId);
+ mAuthClient = new ClientMonitor(token, receiver, groupId, restricted);
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
@@ -344,7 +337,6 @@
mAuthClient = null;
return;
}
- final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.authenticate(opId, groupId);
if (result != 0) {
@@ -378,13 +370,14 @@
}
void startRemove(IBinder token, int fingerId, int userId,
- IFingerprintServiceReceiver receiver) {
+ IFingerprintServiceReceiver receiver, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startRemove: no fingeprintd!");
return;
}
- mRemoveClient = new ClientMonitor(token, receiver, userId);
+
+ mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
@@ -404,6 +397,11 @@
return mFingerprintUtils.getFingerprintsForUser(mContext, groupId).size() > 0;
}
+ boolean hasPermission(String permission) {
+ return getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
void checkPermission(String permission) {
getContext().enforceCallingOrSelfPermission(permission,
"Must have " + permission + " permission.");
@@ -420,11 +418,14 @@
IBinder token;
IFingerprintServiceReceiver receiver;
int userId;
+ boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
- public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+ public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
+ boolean restricted) {
this.token = token;
this.receiver = receiver;
this.userId = userId;
+ this.restricted = restricted;
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -498,7 +499,13 @@
boolean result = false;
if (receiver != null) {
try {
- receiver.onAuthenticated(mHalDeviceId, fpId, groupId);
+ if (fpId == 0) {
+ receiver.onAuthenticationFailed(mHalDeviceId);
+ } else {
+ Fingerprint fp = !restricted ?
+ new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
+ receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
result = true; // client failed
@@ -592,14 +599,22 @@
final IFingerprintServiceReceiver receiver, final int flags) {
checkPermission(MANAGE_FINGERPRINT);
final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
+
+ final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnrollment(token, cryptoClone, groupId, receiver, flags);
+ startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
}
});
}
+ private boolean isRestricted() {
+ // Only give privileged apps (like Settings) access to fingerprint info
+ final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+ return restricted;
+ }
+
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
@@ -614,14 +629,15 @@
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags, String opPackageName) {
- checkPermission(USE_FINGERPRINT);
+
if (!canUseFingerprint(opPackageName)) {
return;
}
+ final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
- startAuthentication(token, opId, groupId, receiver, flags);
+ startAuthentication(token, opId, groupId, receiver, flags, restricted);
}
});
}
@@ -643,10 +659,11 @@
public void remove(final IBinder token, final int fingerId, final int groupId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
+ final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
- startRemove(token, fingerId, groupId, receiver);
+ startRemove(token, fingerId, groupId, receiver, restricted);
}
});
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
index 33177b4..902d970 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
@@ -57,7 +57,7 @@
private final File mFile;
@GuardedBy("this")
- private final ArrayList<Fingerprint> mFingerprints = new ArrayList<>();
+ private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>();
private final Context mCtx;
public FingerprintsUserState(Context ctx, int userId) {
@@ -127,7 +127,7 @@
}
private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
- ArrayList<Fingerprint> result = new ArrayList<>(array.size());
+ ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
for (int i = 0; i < array.size(); i++) {
Fingerprint fp = array.get(i);
result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index fb98d94..ef7be30 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -454,6 +454,18 @@
return mInstaller.execute(builder.toString());
}
+
+ public int linkFile(String relativePath, String fromBase, String toBase) {
+ StringBuilder builder = new StringBuilder("linkfile");
+ builder.append(' ');
+ builder.append(relativePath);
+ builder.append(' ');
+ builder.append(fromBase);
+ builder.append(' ');
+ builder.append(toBase);
+ return mInstaller.execute(builder.toString());
+ }
+
/**
* Returns true iff. {@code instructionSet} is a valid instruction set.
*/
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1cec750..b5ef3b79 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -166,6 +166,8 @@
@GuardedBy("mLock")
private final List<File> mResolvedInheritedFiles = new ArrayList<>();
@GuardedBy("mLock")
+ private final List<String> mResolvedInstructionSets = new ArrayList<>();
+ @GuardedBy("mLock")
private File mInheritedFilesBase;
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@@ -521,7 +523,11 @@
}
if (isLinkPossible(fromFiles, toDir)) {
- createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase);
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ createOatDirs(mResolvedInstructionSets, oatDir);
+ }
+ linkFiles(fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
@@ -706,21 +712,23 @@
final File oatDir = new File(packageInstallDir, "oat");
if (oatDir.exists()) {
final File[] archSubdirs = oatDir.listFiles();
- // Only add "oatDir" if it contains arch specific subdirs.
- if (archSubdirs != null && archSubdirs.length > 0) {
- mResolvedInheritedFiles.add(oatDir);
- }
- final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
- for (File archSubDir : archSubdirs) {
- // Skip any directory that isn't an ISA subdir.
- if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
- continue;
- }
- List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
- if (!oatFiles.isEmpty()) {
- mResolvedInheritedFiles.add(archSubDir);
- mResolvedInheritedFiles.addAll(oatFiles);
+ // Keep track of all instruction sets we've seen compiled output for.
+ // If we're linking (and not copying) inherited files, we can recreate the
+ // instruction set hierarchy and link compiled output.
+ if (archSubdirs != null && archSubdirs.length > 0) {
+ final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
+ for (File archSubDir : archSubdirs) {
+ // Skip any directory that isn't an ISA subdir.
+ if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
+ continue;
+ }
+
+ mResolvedInstructionSets.add(archSubDir.getName());
+ List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
+ if (!oatFiles.isEmpty()) {
+ mResolvedInheritedFiles.addAll(oatFiles);
+ }
}
}
}
@@ -802,71 +810,41 @@
return true;
}
- /**
- * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file}
- * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have
- * relative path components such as {@code "."} or {@code ".."}. For example, for we will
- * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo}
- * and {@code newBase} was {@code /foo2}.
- */
- private static File reparentPath(File file, File oldBase, File newBase) throws IOException {
- final String oldBaseStr = oldBase.getAbsolutePath();
+ private static String getRelativePath(File file, File base) throws IOException {
final String pathStr = file.getAbsolutePath();
-
+ final String baseStr = base.getAbsolutePath();
// Don't allow relative paths.
if (pathStr.contains("/.") ) {
throw new IOException("Invalid path (was relative) : " + pathStr);
}
- if (pathStr.startsWith(oldBaseStr)) {
- final String relative = pathStr.substring(oldBaseStr.length());
- return new File(newBase, relative);
+ if (pathStr.startsWith(baseStr)) {
+ return pathStr.substring(baseStr.length());
}
- throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr);
+ throw new IOException("File: " + pathStr + " outside base: " + baseStr);
}
- /**
- * Recreates a directory and file structure, specified by a list of files {@code fromFiles}
- * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the
- * same permissions, and regular files are linked.
- *
- * TODO: Move this function to installd so that the system process doesn't have to
- * manipulate / relabel directories.
- */
- private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir)
+ private void createOatDirs(List<String> instructionSets, File fromDir) {
+ for (String instructionSet : instructionSets) {
+ mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ }
+ }
+
+ private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
throws IOException {
for (File fromFile : fromFiles) {
- final File toFile = reparentPath(fromFile, fromDir, toDir);
- final StructStat stat;
- try {
- stat = Os.stat(fromFile.getAbsolutePath());
- } catch (ErrnoException e) {
- throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e);
- }
+ final String relativePath = getRelativePath(fromFile, fromDir);
+ final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
+ toDir.getAbsolutePath());
- if (OsConstants.S_ISDIR(stat.st_mode)) {
- if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath());
- try {
- Os.mkdir(toFile.getAbsolutePath(), stat.st_mode);
- } catch (ErrnoException e) {
- throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e);
- }
-
- // We do this to ensure that the oat/ directory is created with the right
- // label (data_dalvikcache_file) instead of apk_tmpfile.
- if (!SELinux.restorecon(toFile)) {
- throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath());
- }
- } else {
- if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
- try {
- Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
- } catch (ErrnoException e) {
- throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
- }
+ if (ret < 0) {
+ // installd will log failure details.
+ throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
+ + fromDir + ", " + toDir + ")");
}
}
+
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 4b62c40..dd8648d 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -67,6 +67,12 @@
private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
private static final int MAX_RADIO_WAIT_TIME = 12*1000;
private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
+ // constants for progress bar. the values are roughly estimated based on timeout.
+ private static final int BROADCAST_STOP_PERCENT = 2;
+ private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
+ private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
+ private static final int RADIO_STOP_PERCENT = 18;
+ private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
// length of vibration before shutting down
private static final int SHUTDOWN_VIBRATE_MS = 500;
@@ -75,11 +81,13 @@
private static Object sIsStartedGuard = new Object();
private static boolean sIsStarted = false;
- // uncrypt status file
+ // uncrypt status files
private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
+ private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
private static boolean mReboot;
private static boolean mRebootSafeMode;
+ private static boolean mRebootUpdate;
private static String mRebootReason;
// Provides shutdown assurance in case the system_server is killed
@@ -203,6 +211,7 @@
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
+ mRebootUpdate = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
@@ -222,6 +231,7 @@
mReboot = true;
mRebootSafeMode = true;
+ mRebootUpdate = false;
mRebootReason = null;
shutdownInner(context, confirm);
}
@@ -235,16 +245,44 @@
sIsStarted = true;
}
- // throw up an indeterminate system dialog to indicate radio is
- // shutting down.
+ // Throw up a system dialog to indicate the device is rebooting / shutting down.
ProgressDialog pd = new ProgressDialog(context);
+
+ // Path 1: Reboot to recovery and install the update
+ // Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
+ // (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
+ // UI: progress bar
+ //
+ // Path 2: Reboot to recovery for factory reset
+ // Condition: mRebootReason == REBOOT_RECOVERY
+ // UI: spinning circle only (no progress bar)
+ //
+ // Path 3: Regular reboot / shutdown
+ // Condition: Otherwise
+ // UI: spinning circle only (no progress bar)
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
- pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title));
+ mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
+ if (mRebootUpdate) {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
+ pd.setMessage(context.getText(
+ com.android.internal.R.string.reboot_to_update_prepare));
+ pd.setMax(100);
+ pd.setProgressNumberFormat(null);
+ pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ pd.setProgress(0);
+ pd.setIndeterminate(false);
+ } else {
+ // Factory reset path. Set the dialog message accordingly.
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
+ pd.setMessage(context.getText(
+ com.android.internal.R.string.reboot_to_reset_message));
+ pd.setIndeterminate(true);
+ }
} else {
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ pd.setIndeterminate(true);
}
- pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
- pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -339,13 +377,20 @@
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
+ } else if (mRebootUpdate) {
+ int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
+ BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
+ sInstance.setRebootProgress(status, null);
}
try {
- mActionDoneSync.wait(delay);
+ mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
} catch (InterruptedException e) {
}
}
}
+ if (mRebootUpdate) {
+ sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
+ }
Log.i(TAG, "Shutting down activity manager...");
@@ -357,6 +402,9 @@
} catch (RemoteException e) {
}
}
+ if (mRebootUpdate) {
+ sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
+ }
Log.i(TAG, "Shutting down package manager...");
@@ -365,9 +413,15 @@
if (pm != null) {
pm.shutdown();
}
+ if (mRebootUpdate) {
+ sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
+ }
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
+ if (mRebootUpdate) {
+ sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
+ }
// Shutdown MountService to ensure media is in a safe state
IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
@@ -399,64 +453,44 @@
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
+ } else if (mRebootUpdate) {
+ int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
+ (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
+ MAX_SHUTDOWN_WAIT_TIME);
+ status += RADIO_STOP_PERCENT;
+ sInstance.setRebootProgress(status, null);
}
try {
- mActionDoneSync.wait(delay);
+ mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
} catch (InterruptedException e) {
}
}
}
+ if (mRebootUpdate) {
+ sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
- // If it's to reboot into recovery, invoke uncrypt via init service.
- if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
+ // If it's to reboot to install update, invoke uncrypt via init service.
uncrypt();
}
rebootOrShutdown(mContext, mReboot, mRebootReason);
}
- private void prepareUncryptProgress() {
- // Reset the dialog message to show the decrypt process.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- }
- // It doesn't work to change the style of the existing
- // one. Have to create a new one.
- ProgressDialog pd = new ProgressDialog(mContext);
-
- pd.setTitle(mContext.getText(
- com.android.internal.R.string.reboot_to_recovery_title));
- pd.setMessage(mContext.getText(
- com.android.internal.R.string.reboot_to_recovery_progress));
- pd.setIndeterminate(false);
- pd.setMax(100);
- pd.setCancelable(false);
- pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- pd.setProgressNumberFormat(null);
- pd.setProgress(0);
-
- mProgressDialog = pd;
- mProgressDialog.show();
- }
- });
- }
-
- private void setUncryptProgress(final int progress) {
+ private void setRebootProgress(final int progress, final CharSequence message) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mProgressDialog != null) {
mProgressDialog.setProgress(progress);
+ if (message != null) {
+ mProgressDialog.setMessage(message);
+ }
}
}
});
}
- private void shutdownRadios(int timeout) {
+ private void shutdownRadios(final int timeout) {
// If a radio is wedged, disabling it may hang so we do this work in another thread,
// just in case.
final long endTime = SystemClock.elapsedRealtime() + timeout;
@@ -511,7 +545,15 @@
Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
- while (SystemClock.elapsedRealtime() < endTime) {
+ long delay = endTime - SystemClock.elapsedRealtime();
+ while (delay > 0) {
+ if (mRebootUpdate) {
+ int status = (int)((timeout - delay) * 1.0 *
+ (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
+ status += PACKAGE_MANAGER_STOP_PERCENT;
+ sInstance.setRebootProgress(status, null);
+ }
+
if (!bluetoothOff) {
try {
bluetoothOff = !bluetooth.isEnabled();
@@ -552,6 +594,8 @@
break;
}
SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
+
+ delay = endTime - SystemClock.elapsedRealtime();
}
}
};
@@ -604,9 +648,6 @@
private void uncrypt() {
Log.i(TAG, "Calling uncrypt and monitoring the progress...");
- // Update the ProcessDialog message and style.
- sInstance.prepareUncryptProgress();
-
final boolean[] done = new boolean[1];
done[0] = false;
Thread t = new Thread() {
@@ -627,25 +668,32 @@
try (BufferedReader reader = new BufferedReader(
new FileReader(UNCRYPT_STATUS_FILE))) {
- int last_status = Integer.MIN_VALUE;
+ int lastStatus = Integer.MIN_VALUE;
while (true) {
String str = reader.readLine();
try {
int status = Integer.parseInt(str);
// Avoid flooding the log with the same message.
- if (status == last_status && last_status != Integer.MIN_VALUE) {
+ if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
continue;
}
- last_status = status;
+ lastStatus = status;
if (status >= 0 && status < 100) {
// Update status
Log.d(TAG, "uncrypt read status: " + status);
- sInstance.setUncryptProgress(status);
+ // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
+ status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
+ status += MOUNT_SERVICE_STOP_PERCENT;
+ CharSequence msg = mContext.getText(
+ com.android.internal.R.string.reboot_to_update_package);
+ sInstance.setRebootProgress(status, msg);
} else if (status == 100) {
Log.d(TAG, "uncrypt successfully finished.");
- sInstance.setUncryptProgress(status);
+ CharSequence msg = mContext.getText(
+ com.android.internal.R.string.reboot_to_update_reboot);
+ sInstance.setRebootProgress(status, msg);
break;
} else {
// Error in /system/bin/uncrypt. Or it's rebooting to recovery
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6dc1183..d7b202d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -343,8 +343,7 @@
boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
&& !winAnimator.mKeyguardGoingAwayAnimation
&& win.hasDrawnLw()
- && win.mAttachedWindow == null
- && mForceHiding != KEYGUARD_NOT_SHOWN;
+ && win.mAttachedWindow == null;
// If the window is already showing and we don't need to apply an existing
// Keyguard exit animation, skip.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index eda7f79..b918a25 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -640,6 +640,16 @@
// Now make sure the window fits in the overall display frame.
Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame);
+ // Calculate the outsets before the content frame gets shrinked to the window frame.
+ if (hasOutsets) {
+ mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
+ Math.max(mContentFrame.top - mOutsetFrame.top, 0),
+ Math.max(mOutsetFrame.right - mContentFrame.right, 0),
+ Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
+ } else {
+ mOutsets.set(0, 0, 0, 0);
+ }
+
// Make sure the content and visible frames are inside of the
// final window frame.
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
@@ -677,16 +687,6 @@
Math.max(mFrame.right - mStableFrame.right, 0),
Math.max(mFrame.bottom - mStableFrame.bottom, 0));
- if (hasOutsets) {
- // We need to calculate outsets
- mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
- Math.max(mContentFrame.top - mOutsetFrame.top, 0),
- Math.max(mOutsetFrame.right - mContentFrame.right, 0),
- Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
- } else {
- mOutsets.set(0, 0, 0, 0);
- }
-
mCompatFrame.set(mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index cb70144..06b9bc3 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -36,6 +36,7 @@
{
static jclass sFileDescriptorClass;
+static jfieldID sPipeFDField;
static jint
android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */,
@@ -66,14 +67,15 @@
}
static jobjectArray
-android_server_UsbMidiDevice_open(JNIEnv *env, jobject /* thiz */, jint card, jint device,
+android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device,
jint subdevice_count)
{
char path[100];
snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device);
- jobjectArray fds = env->NewObjectArray(subdevice_count, sFileDescriptorClass, NULL);
+ // allocate one extra file descriptor for close pipe
+ jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL);
if (!fds) {
return NULL;
}
@@ -91,12 +93,27 @@
env->DeleteLocalRef(fileDescriptor);
}
+ // create a pipe to use for unblocking our input thread
+ int pipeFD[2];
+ pipe(pipeFD);
+ jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
+ env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
+ env->DeleteLocalRef(fileDescriptor);
+ // store our end of the pipe in mPipeFD
+ env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+
return fds;
}
static void
-android_server_UsbMidiDevice_close(JNIEnv *env, jobject /* thiz */, jobjectArray fds)
+android_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds)
{
+ // write to mPipeFD to unblock input thread
+ jint pipeFD = env->GetIntField(thiz, sPipeFDField);
+ write(pipeFD, &pipeFD, sizeof(pipeFD));
+ close(pipeFD);
+ env->SetIntField(thiz, sPipeFDField, -1);
+
int count = env->GetArrayLength(fds);
for (int i = 0; i < count; i++) {
jobject fd = env->GetObjectArrayElement(fds, i);
@@ -117,13 +134,18 @@
ALOGE("Can't find java/io/FileDescriptor");
return -1;
}
- sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz);;
+ sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz);
clazz = env->FindClass("com/android/server/usb/UsbMidiDevice");
if (clazz == NULL) {
ALOGE("Can't find com/android/server/usb/UsbMidiDevice");
return -1;
}
+ sPipeFDField = env->GetFieldID(clazz, "mPipeFD", "I");
+ if (sPipeFDField == NULL) {
+ ALOGE("Can't find UsbMidiDevice.mPipeFD");
+ return -1;
+ }
return jniRegisterNativeMethods(env, "com/android/server/usb/UsbMidiDevice",
method_table, NELEM(method_table));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 825ef1a..feb0285 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -183,7 +183,6 @@
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
- private static final String ATTR_PREFERRED_SETUP_ACTIVITY = "setup-activity";
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
@@ -335,8 +334,6 @@
boolean doNotAskCredentialsOnBoot = false;
- ComponentName mPreferredSetupActivity;
-
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -1436,12 +1433,6 @@
out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
policy.mDelegatedCertInstallerPackage);
}
- if (policy.mPreferredSetupActivity != null) {
- out.attribute(null, ATTR_PREFERRED_SETUP_ACTIVITY,
- policy.mPreferredSetupActivity.flattenToString());
- } else {
- out.attribute(null, ATTR_PREFERRED_SETUP_ACTIVITY, "");
- }
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
@@ -1566,12 +1557,6 @@
}
policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
ATTR_DELEGATED_CERT_INSTALLER);
- String preferredSetupActivity =
- parser.getAttributeValue(null, ATTR_PREFERRED_SETUP_ACTIVITY);
- if (preferredSetupActivity != null) {
- policy.mPreferredSetupActivity =
- ComponentName.unflattenFromString(preferredSetupActivity);
- }
type = parser.next();
int outerDepth = parser.getDepth();
@@ -1695,7 +1680,6 @@
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
- updatePreferredSetupActivityLocked(userHandle);
}
private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
@@ -4734,43 +4718,6 @@
}
@Override
- public void setPreferredSetupActivity(ComponentName who, ComponentName activity) {
- if (!mHasFeature) {
- return;
- }
- Preconditions.checkNotNull(who, "ComponentName is null");
- synchronized (this) {
- ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (!isDeviceInitializer(activeAdmin.info.getPackageName())) {
- throw new SecurityException(
- "This method can only be called by device initializers");
- }
- int userHandle = UserHandle.getCallingUserId();
- DevicePolicyData userData = getUserData(userHandle);
- userData.mPreferredSetupActivity = activity;
- saveSettingsLocked(userHandle);
- updatePreferredSetupActivityLocked(userHandle);
- }
- }
-
- private void updatePreferredSetupActivityLocked(int userHandle) {
- if (!mHasFeature) {
- return;
- }
- IActivityManager am = ActivityManagerNative.getDefault();
- long ident = Binder.clearCallingIdentity();
- try {
- am.updatePreferredSetupActivity(
- getUserData(userHandle).mPreferredSetupActivity, userHandle);
- } catch (RemoteException e) {
- // Not gonna happen.
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
Preconditions.checkNotNull(who, "ComponentName is null");
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
@@ -6138,9 +6085,6 @@
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
synchronized (this) {
- // Clear the preferred setup activity.
- policy.mPreferredSetupActivity = null;
- updatePreferredSetupActivityLocked(userHandle);
// The DeviceInitializer was whitelisted but now should be removed.
removeDeviceInitializerFromLockTaskPackages(userHandle);
saveSettingsLocked(userHandle);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 23057c4b..633aee8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -57,6 +57,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
@@ -725,6 +726,10 @@
return false;
}
+ if (isCarrierApp(packageName)) {
+ return false;
+ }
+
if (mAppWidgetManager != null
&& mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
return false;
@@ -754,6 +759,12 @@
return false;
}
+ private boolean isCarrierApp(String packageName) {
+ TelephonyManager telephonyManager = getContext().getSystemService(TelephonyManager.class);
+ return telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
+
void informListeners(String packageName, int userId, boolean isIdle) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, isIdle);
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 671cf01..97bf505 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
import android.media.midi.MidiManager;
import android.media.midi.MidiReceiver;
import android.media.midi.MidiSender;
@@ -43,38 +44,100 @@
public final class UsbMidiDevice implements Closeable {
private static final String TAG = "UsbMidiDevice";
+ private final int mAlsaCard;
+ private final int mAlsaDevice;
+ private final int mSubdeviceCount;
+ private final InputReceiverProxy[] mInputPortReceivers;
+
private MidiDeviceServer mServer;
// event schedulers for each output port
- private final MidiEventScheduler[] mEventSchedulers;
+ private MidiEventScheduler[] mEventSchedulers;
private static final int BUFFER_SIZE = 512;
- private final FileDescriptor[] mFileDescriptors;
+ private FileDescriptor[] mFileDescriptors;
// for polling multiple FileDescriptors for MIDI events
- private final StructPollfd[] mPollFDs;
+ private StructPollfd[] mPollFDs;
// streams for reading from ALSA driver
- private final FileInputStream[] mInputStreams;
+ private FileInputStream[] mInputStreams;
// streams for writing to ALSA driver
- private final FileOutputStream[] mOutputStreams;
+ private FileOutputStream[] mOutputStreams;
+ private final Object mLock = new Object();
+ private boolean mIsOpen;
+
+ // pipe file descriptor for signalling input thread to exit
+ // only accessed from JNI code
+ private int mPipeFD = -1;
+
+ private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
+
+ @Override
+ public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+ MidiDeviceInfo deviceInfo = status.getDeviceInfo();
+ int inputPorts = deviceInfo.getInputPortCount();
+ int outputPorts = deviceInfo.getOutputPortCount();
+ boolean hasOpenPorts = false;
+
+ for (int i = 0; i < inputPorts; i++) {
+ if (status.isInputPortOpen(i)) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+
+ if (!hasOpenPorts) {
+ for (int i = 0; i < outputPorts; i++) {
+ if (status.getOutputPortOpenCount(i) > 0) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ if (hasOpenPorts && !mIsOpen) {
+ openLocked();
+ } else if (!hasOpenPorts && mIsOpen) {
+ closeLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onClose() {
+ }
+ };
+
+ // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist
+ // until the device has active clients
+ private final class InputReceiverProxy extends MidiReceiver {
+ private MidiReceiver mReceiver;
+
+ @Override
+ public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ MidiReceiver receiver = mReceiver;
+ if (receiver != null) {
+ receiver.send(msg, offset, count, timestamp);
+ }
+ }
+
+ public void setReceiver(MidiReceiver receiver) {
+ mReceiver = receiver;
+ }
+ }
+
public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
// FIXME - support devices with different number of input and output ports
- int subDevices = nativeGetSubdeviceCount(card, device);
- if (subDevices <= 0) {
+ int subDeviceCount = nativeGetSubdeviceCount(card, device);
+ if (subDeviceCount <= 0) {
Log.e(TAG, "nativeGetSubdeviceCount failed");
return null;
}
- // FIXME - support devices with different number of input and output ports
- FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices);
- if (fileDescriptors == null) {
- Log.e(TAG, "nativeOpen failed");
- return null;
- }
-
- UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors);
+ UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount);
if (!midiDevice.register(context, properties)) {
IoUtils.closeQuietly(midiDevice);
Log.e(TAG, "createDeviceServer failed");
@@ -83,10 +146,32 @@
return midiDevice;
}
- private UsbMidiDevice(FileDescriptor[] fileDescriptors) {
+ private UsbMidiDevice(int card, int device, int subdeviceCount) {
+ mAlsaCard = card;
+ mAlsaDevice = device;
+ mSubdeviceCount = subdeviceCount;
+
+ // FIXME - support devices with different number of input and output ports
+ int inputCount = subdeviceCount;
+ mInputPortReceivers = new InputReceiverProxy[inputCount];
+ for (int port = 0; port < inputCount; port++) {
+ mInputPortReceivers[port] = new InputReceiverProxy();
+ }
+ }
+
+ private boolean openLocked() {
+ // FIXME - support devices with different number of input and output ports
+ FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount);
+ if (fileDescriptors == null) {
+ Log.e(TAG, "nativeOpen failed");
+ return false;
+ }
+
mFileDescriptors = fileDescriptors;
int inputCount = fileDescriptors.length;
- int outputCount = fileDescriptors.length;
+ // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
+ // in our input thread
+ int outputCount = fileDescriptors.length - 1;
mPollFDs = new StructPollfd[inputCount];
mInputStreams = new FileInputStream[inputCount];
@@ -103,29 +188,12 @@
mEventSchedulers = new MidiEventScheduler[outputCount];
for (int i = 0; i < outputCount; i++) {
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
- mEventSchedulers[i] = new MidiEventScheduler();
- }
- }
- private boolean register(Context context, Bundle properties) {
- MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
- if (midiManager == null) {
- Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
- return false;
+ MidiEventScheduler scheduler = new MidiEventScheduler();
+ mEventSchedulers[i] = scheduler;
+ mInputPortReceivers[i].setReceiver(scheduler.getReceiver());
}
- int inputCount = mInputStreams.length;
- int outputCount = mOutputStreams.length;
- MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
- for (int port = 0; port < inputCount; port++) {
- inputPortReceivers[port] = mEventSchedulers[port].getReceiver();
- }
-
- mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
- null, null, properties, MidiDeviceInfo.TYPE_USB, null);
- if (mServer == null) {
- return false;
- }
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
// Create input thread which will read from all input ports
@@ -134,24 +202,32 @@
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
try {
- boolean done = false;
- while (!done) {
- // look for a readable FileDescriptor
- for (int index = 0; index < mPollFDs.length; index++) {
- StructPollfd pfd = mPollFDs[index];
- if ((pfd.revents & OsConstants.POLLIN) != 0) {
- // clear readable flag
- pfd.revents = 0;
+ while (true) {
+ synchronized (mLock) {
+ if (!mIsOpen) break;
- int count = mInputStreams[index].read(buffer);
- outputReceivers[index].send(buffer, 0, count);
- } else if ((pfd.revents & (OsConstants.POLLERR
- | OsConstants.POLLHUP)) != 0) {
- done = true;
+ // look for a readable FileDescriptor
+ for (int index = 0; index < mPollFDs.length; index++) {
+ StructPollfd pfd = mPollFDs[index];
+ if ((pfd.revents & (OsConstants.POLLERR
+ | OsConstants.POLLHUP)) != 0) {
+ break;
+ } else if ((pfd.revents & OsConstants.POLLIN) != 0) {
+ // clear readable flag
+ pfd.revents = 0;
+
+ if (index == mInputStreams.length - 1) {
+ // last file descriptor is used only for unblocking Os.poll()
+ break;
+ }
+
+ int count = mInputStreams[index].read(buffer);
+ outputReceivers[index].send(buffer, 0, count);
+ }
}
}
- // wait until we have a readable port
+ // wait until we have a readable port or we are signalled to close
Os.poll(mPollFDs, -1 /* infinite timeout */);
}
} catch (IOException e) {
@@ -195,29 +271,64 @@
}.start();
}
+ mIsOpen = true;
+ return true;
+ }
+
+ private boolean register(Context context, Bundle properties) {
+ MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
+ if (midiManager == null) {
+ Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
+ return false;
+ }
+
+ mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount,
+ null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
+ if (mServer == null) {
+ return false;
+ }
+
return true;
}
@Override
public void close() throws IOException {
- for (int i = 0; i < mEventSchedulers.length; i++) {
- mEventSchedulers[i].close();
+ synchronized (mLock) {
+ if (mIsOpen) {
+ closeLocked();
+ }
}
if (mServer != null) {
- mServer.close();
+ IoUtils.closeQuietly(mServer);
}
+ }
+
+ private void closeLocked() {
+ for (int i = 0; i < mEventSchedulers.length; i++) {
+ mInputPortReceivers[i].setReceiver(null);
+ mEventSchedulers[i].close();
+ }
+ mEventSchedulers = null;
for (int i = 0; i < mInputStreams.length; i++) {
- mInputStreams[i].close();
+ IoUtils.closeQuietly(mInputStreams[i]);
}
+ mInputStreams = null;
+
for (int i = 0; i < mOutputStreams.length; i++) {
- mOutputStreams[i].close();
+ IoUtils.closeQuietly(mOutputStreams[i]);
}
+ mOutputStreams = null;
+
+ // nativeClose will close the file descriptors and signal the input thread to exit
nativeClose(mFileDescriptors);
+ mFileDescriptors = null;
+
+ mIsOpen = false;
}
private static native int nativeGetSubdeviceCount(int card, int device);
- private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
- private static native void nativeClose(FileDescriptor[] fileDescriptors);
+ private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
+ private native void nativeClose(FileDescriptor[] fileDescriptors);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8834497..4cdf254 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -49,6 +49,7 @@
import android.service.voice.VoiceInteractionSession;
import android.speech.RecognitionService;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -475,11 +476,9 @@
Slog.w(TAG, "hideSessionFromSession without running voice interaction service");
return false;
}
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.hideSessionLocked(callingPid, callingUid);
+ return mImpl.hideSessionLocked();
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -744,6 +743,28 @@
}
@Override
+ public void hideCurrentSession() throws RemoteException {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ synchronized (this) {
+ if (mImpl == null) {
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) {
+ try {
+ mImpl.mActiveSession.mSession.closeSystemDialogs();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call closeSystemDialogs", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
public void launchVoiceAssistFromKeyguard() {
enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
synchronized (this) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index d8569bc..acd484d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -144,7 +144,7 @@
return mActiveSession.showLocked(args, flags, showCallback);
}
- public boolean hideSessionLocked(int callingPid, int callingUid) {
+ public boolean hideSessionLocked() {
if (mActiveSession != null) {
return mActiveSession.hideLocked();
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bb210f1..0042414 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -838,7 +838,8 @@
* <p>
* This could be in response to a preview request via
* {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
- * {@link VideoProvider}.
+ * {@link VideoProvider}. Where periodic updates of data usage are provided, they should be
+ * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
* <p>
* Received by the {@link InCallService} via
* {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index f7f4425..fb985ce 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -413,6 +413,8 @@
/**
* Clears the video call callback set via {@link #registerCallback}.
+ *
+ * @param callback The video call callback to clear.
*/
public abstract void unregisterCallback(VideoCall.Callback callback);
@@ -524,7 +526,8 @@
/**
* The {@link InCallService} extends this class to provide a means of receiving callbacks
- * from the {@link Connection.VideoProvider}.<p>
+ * from the {@link Connection.VideoProvider}.
+ * <p>
* When the {@link InCallService} receives the
* {@link Call.Callback#onVideoCallChanged(Call, VideoCall)} callback, it should create an
* instance its {@link VideoCall.Callback} implementation and set it on the
@@ -533,7 +536,7 @@
public static abstract class Callback {
/**
* Called when the {@link Connection.VideoProvider} receives a session modification
- * request is received from the peer device.
+ * request from the peer device.
* <p>
* The {@link InCallService} may potentially prompt the user to confirm whether they
* wish to accept the request, or decide to automatically accept the request. In either
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index d62c08e..8f7b82f 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.hardware.camera2.CameraManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -207,29 +208,111 @@
public void onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras) {}
}
+ /**
+ * {@link RemoteConnection.VideoProvider} associated with a {@link RemoteConnection}. Used to
+ * receive video related events and control the video associated with a
+ * {@link RemoteConnection}.
+ *
+ * @see Connection.VideoProvider
+ */
public static class VideoProvider {
+ /**
+ * Callback class used by the {@link RemoteConnection.VideoProvider} to relay events from
+ * the {@link Connection.VideoProvider}.
+ */
public abstract static class Callback {
+ /**
+ * Reports a session modification request received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param videoProfile The requested video call profile.
+ * @see InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)
+ * @see Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)
+ */
public void onSessionModifyRequestReceived(
VideoProvider videoProvider,
VideoProfile videoProfile) {}
+ /**
+ * Reports a session modification response received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param status Status of the session modify request.
+ * @param requestedProfile The original request which was sent to the peer device.
+ * @param responseProfile The actual profile changes made by the peer device.
+ * @see InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
+ * VideoProfile, VideoProfile)
+ * @see Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
+ * VideoProfile)
+ */
public void onSessionModifyResponseReceived(
VideoProvider videoProvider,
int status,
VideoProfile requestedProfile,
VideoProfile responseProfile) {}
+ /**
+ * Reports a call session event received from the {@link Connection.VideoProvider}
+ * associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param event The event.
+ * @see InCallService.VideoCall.Callback#onCallSessionEvent(int)
+ * @see Connection.VideoProvider#handleCallSessionEvent(int)
+ */
public void onCallSessionEvent(VideoProvider videoProvider, int event) {}
- public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
+ /**
+ * Reports a change in the peer video dimensions received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param width The updated peer video width.
+ * @param height The updated peer video height.
+ * @see InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)
+ * @see Connection.VideoProvider#changePeerDimensions(int, int)
+ */
+ public void onPeerDimensionsChanged(VideoProvider videoProvider, int width,
+ int height) {}
+ /**
+ * Reports a change in the data usage (in bytes) received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param dataUsage The updated data usage (in bytes).
+ * @see InCallService.VideoCall.Callback#onCallDataUsageChanged(long)
+ * @see Connection.VideoProvider#setCallDataUsage(long)
+ */
public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
+ /**
+ * Reports a change in the capabilities of the current camera, received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param cameraCapabilities The changed camera capabilities.
+ * @see InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
+ * VideoProfile.CameraCapabilities)
+ * @see Connection.VideoProvider#changeCameraCapabilities(
+ * VideoProfile.CameraCapabilities)
+ */
public void onCameraCapabilitiesChanged(
VideoProvider videoProvider,
VideoProfile.CameraCapabilities cameraCapabilities) {}
+ /**
+ * Reports a change in the video quality received from the
+ * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+ *
+ * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+ * @param videoQuality The updated peer video quality.
+ * @see InCallService.VideoCall.Callback#onVideoQualityChanged(int)
+ * @see Connection.VideoProvider#changeVideoQuality(int)
+ */
public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
}
@@ -316,14 +399,32 @@
}
}
+ /**
+ * Registers a callback to receive commands and state changes for video calls.
+ *
+ * @param l The video call callback.
+ */
public void registerCallback(Callback l) {
mCallbacks.add(l);
}
+ /**
+ * Clears the video call callback set via {@link #registerCallback}.
+ *
+ * @param l The video call callback to clear.
+ */
public void unregisterCallback(Callback l) {
mCallbacks.remove(l);
}
+ /**
+ * Sets the camera to be used for the outgoing video for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @param cameraId The id of the camera (use ids as reported by
+ * {@link CameraManager#getCameraIdList()}).
+ * @see Connection.VideoProvider#onSetCamera(String)
+ */
public void setCamera(String cameraId) {
try {
mVideoProviderBinder.setCamera(cameraId);
@@ -331,6 +432,13 @@
}
}
+ /**
+ * Sets the surface to be used for displaying a preview of what the user's camera is
+ * currently capturing for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param surface The {@link Surface}.
+ * @see Connection.VideoProvider#onSetPreviewSurface(Surface)
+ */
public void setPreviewSurface(Surface surface) {
try {
mVideoProviderBinder.setPreviewSurface(surface);
@@ -338,6 +446,13 @@
}
}
+ /**
+ * Sets the surface to be used for displaying the video received from the remote device for
+ * the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param surface The {@link Surface}.
+ * @see Connection.VideoProvider#onSetDisplaySurface(Surface)
+ */
public void setDisplaySurface(Surface surface) {
try {
mVideoProviderBinder.setDisplaySurface(surface);
@@ -345,6 +460,13 @@
}
}
+ /**
+ * Sets the device orientation, in degrees, for the {@link RemoteConnection.VideoProvider}.
+ * Assumes that a standard portrait orientation of the device is 0 degrees.
+ *
+ * @param rotation The device orientation, in degrees.
+ * @see Connection.VideoProvider#onSetDeviceOrientation(int)
+ */
public void setDeviceOrientation(int rotation) {
try {
mVideoProviderBinder.setDeviceOrientation(rotation);
@@ -352,6 +474,12 @@
}
}
+ /**
+ * Sets camera zoom ratio for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param value The camera zoom ratio.
+ * @see Connection.VideoProvider#onSetZoom(float)
+ */
public void setZoom(float value) {
try {
mVideoProviderBinder.setZoom(value);
@@ -359,6 +487,14 @@
}
}
+ /**
+ * Issues a request to modify the properties of the current video session for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @param fromProfile The video profile prior to the request.
+ * @param toProfile The video profile with the requested changes made.
+ * @see Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)
+ */
public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
try {
mVideoProviderBinder.sendSessionModifyRequest(fromProfile, toProfile);
@@ -366,6 +502,13 @@
}
}
+ /**
+ * Provides a response to a request to change the current call video session
+ * properties for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @param responseProfile The response call video properties.
+ * @see Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)
+ */
public void sendSessionModifyResponse(VideoProfile responseProfile) {
try {
mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
@@ -373,6 +516,12 @@
}
}
+ /**
+ * Issues a request to retrieve the capabilities of the current camera for the
+ * {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onRequestCameraCapabilities()
+ */
public void requestCameraCapabilities() {
try {
mVideoProviderBinder.requestCameraCapabilities();
@@ -380,6 +529,12 @@
}
}
+ /**
+ * Issues a request to retrieve the data usage (in bytes) of the video portion of the
+ * {@link RemoteConnection} for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onRequestConnectionDataUsage()
+ */
public void requestCallDataUsage() {
try {
mVideoProviderBinder.requestCallDataUsage();
@@ -387,6 +542,12 @@
}
}
+ /**
+ * Sets the {@link Uri} of an image to be displayed to the peer device when the video signal
+ * is paused, for the {@link RemoteConnection.VideoProvider}.
+ *
+ * @see Connection.VideoProvider#onSetPauseImage(Uri)
+ */
public void setPauseImage(Uri uri) {
try {
mVideoProviderBinder.setPauseImage(uri);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index fb0ecb0..368e137 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -162,8 +162,8 @@
* The extra used by a {@link ConnectionService} to provide the handle of the caller that
* has initiated a new incoming call.
*/
- public static final String EXTRA_INCOMING_CALL_HANDLE =
- "android.telecom.extra.INCOMING_CALL_HANDLE";
+ public static final String EXTRA_INCOMING_CALL_ADDRESS =
+ "android.telecom.extra.INCOMING_CALL_ADDRESS";
/**
* Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ccfaca7..b74b52d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -380,8 +380,9 @@
/**
* Calling this method triggers telephony services to fetch the current carrier configuration.
* <p>
- * Normally this does not need to be called because the platform reloads config on its own. Call
- * this method if your app wants to update config at an arbitrary moment.
+ * Normally this does not need to be called because the platform reloads config on its own.
+ * This should be called by a carrier service app if it wants to update config at an arbitrary
+ * moment.
* </p>
* <p>
* This method returns before the reload has completed, and
@@ -389,9 +390,9 @@
* arbitrary thread.
* </p>
*/
- public void reloadCarrierConfigForSubId(int subId) {
+ public void notifyConfigChangedForSubId(int subId) {
try {
- getICarrierConfigLoader().reloadCarrierConfigForSubId(subId);
+ getICarrierConfigLoader().notifyConfigChangedForSubId(subId);
} catch (RemoteException ex) {
Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
} catch (NullPointerException ex) {
diff --git a/telephony/java/android/telephony/ModemActivityInfo.aidl b/telephony/java/android/telephony/ModemActivityInfo.aidl
new file mode 100644
index 0000000..b85ef7a
--- /dev/null
+++ b/telephony/java/android/telephony/ModemActivityInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 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.telephony;
+
+parcelable ModemActivityInfo;
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
new file mode 100644
index 0000000..ea96e7c
--- /dev/null
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -0,0 +1,159 @@
+/*
+ * 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Reports modem activity information
+ * @hide
+ */
+public class ModemActivityInfo implements Parcelable {
+ /**
+ * Tx power index
+ * index 0 = tx_power < 0dBm
+ * index 1 = 0dBm < tx_power < 5dBm
+ * index 2 = 5dBm < tx_power < 15dBm
+ * index 3 = 15dBm < tx_power < 20dBm
+ * index 4 = tx_power > 20dBm
+ */
+ public static final int TX_POWER_LEVELS = 5;
+
+ private final long mTimestamp;
+ private final int mSleepTimeMs;
+ private final int mIdleTimeMs;
+ private final int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+ private final int mRxTimeMs;
+ private final int mEnergyUsed;
+
+ public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int[] txTimeMs, int rxTimeMs, int energyUsed) {
+ mTimestamp = timestamp;
+ mSleepTimeMs = sleepTimeMs;
+ mIdleTimeMs = idleTimeMs;
+ System.arraycopy(txTimeMs, 0, mTxTimeMs, 0, Math.min(txTimeMs.length, TX_POWER_LEVELS));
+ mRxTimeMs = rxTimeMs;
+ mEnergyUsed = energyUsed;
+ }
+
+ @Override
+ public String toString() {
+ return "ModemActivityInfo{"
+ + " mTimestamp=" + mTimestamp
+ + " mSleepTimeMs=" + mSleepTimeMs
+ + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ + " mRxTimeMs=" + mRxTimeMs
+ + " mEnergyUsed=" + mEnergyUsed
+ + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ModemActivityInfo> CREATOR =
+ new Parcelable.Creator<ModemActivityInfo>() {
+ public ModemActivityInfo createFromParcel(Parcel in) {
+ long timestamp = in.readLong();
+ int sleepTimeMs = in.readInt();
+ int idleTimeMs = in.readInt();
+ int[] txTimeMs = new int[TX_POWER_LEVELS];
+ for (int i = 0; i < TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = in.readInt();
+ }
+ int rxTimeMs = in.readInt();
+ int energyUsed = in.readInt();
+ return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
+ txTimeMs, rxTimeMs, energyUsed);
+ }
+
+ public ModemActivityInfo[] newArray(int size) {
+ return new ModemActivityInfo[size];
+ }
+ };
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ dest.writeInt(mSleepTimeMs);
+ dest.writeInt(mIdleTimeMs);
+ for (int i = 0; i < TX_POWER_LEVELS; i++) {
+ dest.writeInt(mTxTimeMs[i]);
+ }
+ dest.writeInt(mRxTimeMs);
+ dest.writeInt(mEnergyUsed);
+ }
+
+ /**
+ * @return timestamp of record creation
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * @return tx time in ms. It's an array of tx times
+ * with each index...
+ */
+ public int [] getTxTimeMillis() {
+ return mTxTimeMs;
+ }
+
+ /**
+ * @return sleep time in ms.
+ */
+ public int getSleepTimeMillis() {
+ return mSleepTimeMs;
+ }
+
+ /**
+ * @return idle time in ms.
+ */
+ public int getIdleTimeMillis() {
+ return mIdleTimeMs;
+ }
+
+ /**
+ * @return rx time in ms.
+ */
+ public int getRxTimeMillis() {
+ return mRxTimeMs;
+ }
+
+ /**
+ * product of current(mA), voltage(V) and time(ms)
+ * @return energy used
+ */
+ public int getEnergyUsed () {
+ return mEnergyUsed;
+ }
+
+ /**
+ * @return if the record is valid
+ */
+ public boolean isValid() {
+ int totalTxTimeMs = 0;
+ int txTime [] = getTxTimeMillis();
+ for (int i = 0; i < TX_POWER_LEVELS; i++) {
+ totalTxTimeMs += txTime[i];
+ }
+ return ((getIdleTimeMillis() != 0) || (totalTxTimeMs != 0)
+ || (getSleepTimeMillis() != 0) || (getIdleTimeMillis() != 0));
+ }
+}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index d192288..16472c8 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -120,8 +120,7 @@
/**
* Listen for changes to the device call state.
* {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE}
+ *
* @see #onCallStateChanged
*/
public static final int LISTEN_CALL_STATE = 0x00000020;
@@ -137,8 +136,6 @@
* Listen for changes to the direction of data traffic on the data
* connection (cellular).
* {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE}
* Example: The status bar uses this to display the appropriate
* data-traffic icon.
*
@@ -388,6 +385,10 @@
/**
* Callback invoked when device call state changes.
+ * @param state call state
+ * @param incomingNumber incoming call phone number. If application does not have
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
+ * string will be passed as an argument.
*
* @see TelephonyManager#CALL_STATE_IDLE
* @see TelephonyManager#CALL_STATE_RINGING
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 86f7ec3..1fe0f0b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3658,11 +3658,11 @@
/** @hide */
@SystemApi
- public int checkCarrierPrivilegesForPackage(String pkgname) {
+ public int checkCarrierPrivilegesForPackage(String pkgName) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.checkCarrierPrivilegesForPackage(pkgname);
+ return telephony.checkCarrierPrivilegesForPackage(pkgName);
} catch (RemoteException ex) {
Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
} catch (NullPointerException ex) {
@@ -3673,6 +3673,21 @@
/** @hide */
@SystemApi
+ public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.checkCarrierPrivilegesForPackageAnyPhone(pkgName);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone NPE", ex);
+ }
+ return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ /** @hide */
+ @SystemApi
public List<String> getCarrierPackageNamesForIntent(Intent intent) {
return getCarrierPackageNamesForIntentAndPhone(intent, getDefaultPhone());
}
@@ -4617,7 +4632,22 @@
}
} catch (RemoteException ex) {
}
+ return null;
+ }
+ /**
+ * Returns the modem activity info.
+ * @hide
+ */
+ public ModemActivityInfo getModemActivityInfo() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getModemActivityInfo();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
+ }
return null;
}
}
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
index cb53f51..d77b27f 100644
--- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -25,7 +25,7 @@
PersistableBundle getConfigForSubId(int subId);
- void reloadCarrierConfigForSubId(int subId);
+ void notifyConfigChangedForSubId(int subId);
void updateConfigForPhoneId(int phoneId, String simState);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 11d0ea6..c253b4f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -23,6 +23,7 @@
import android.telephony.IccOpenLogicalChannelResponse;
import android.telephony.NeighboringCellInfo;
import android.telephony.RadioAccessFamily;
+import android.telephony.ModemActivityInfo;
import java.util.List;
@@ -739,9 +740,14 @@
int getCarrierPrivilegeStatus();
/**
- * Similar to above, but check for pkg whose name is pkgname.
+ * Similar to above, but check for the package whose name is pkgName.
*/
- int checkCarrierPrivilegesForPackage(String pkgname);
+ int checkCarrierPrivilegesForPackage(String pkgName);
+
+ /**
+ * Similar to above, but check across all phones.
+ */
+ int checkCarrierPrivilegesForPackageAnyPhone(String pkgName);
/**
* Returns list of the package names of the carrier apps that should handle the input intent
@@ -953,4 +959,10 @@
* if no locale could be derived.
*/
String getLocaleFromDefaultSim();
+
+ /**
+ * Return the modem activity info.
+ *@hide
+ */
+ ModemActivityInfo getModemActivityInfo();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 12541d8..8d48c86 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -323,6 +323,7 @@
int RIL_REQUEST_START_LCE = 132;
int RIL_REQUEST_STOP_LCE = 133;
int RIL_REQUEST_PULL_LCEDATA = 134;
+ int RIL_REQUEST_GET_ACTIVITY_INFO = 135;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index d311cd9..10f8150 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -40,6 +40,7 @@
ManifestParser.cpp \
ManifestValidator.cpp \
Png.cpp \
+ ProguardRules.cpp \
ResChunkPullParser.cpp \
Resource.cpp \
ResourceParser.cpp \
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index de2dafc..41c229d 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -28,6 +28,7 @@
#include "ManifestValidator.h"
#include "NameMangler.h"
#include "Png.h"
+#include "ProguardRules.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceTableResolver.h"
@@ -300,6 +301,9 @@
// Directory to in which to generate R.java.
Maybe<Source> generateJavaClass;
+ // File in which to produce proguard rules.
+ Maybe<Source> generateProguardRules;
+
// Whether to output verbose details about
// compilation.
bool verbose = false;
@@ -417,7 +421,8 @@
bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
- const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
+ const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue,
+ proguard::KeepSet* keepSet) {
SourceLogger logger(item.source);
std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
if (!root) {
@@ -435,6 +440,10 @@
xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
}
+ if (options.generateProguardRules) {
+ proguard::collectProguardRules(item.name.type, item.source, root.get(), keepSet);
+ }
+
BigBuffer outBuffer(1024);
Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
item.originalPackage, resolver,
@@ -509,7 +518,7 @@
bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
- const android::ResTable& table, ZipFile* outApk) {
+ const android::ResTable& table, ZipFile* outApk, proguard::KeepSet* keepSet) {
if (options.verbose) {
Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
}
@@ -557,6 +566,11 @@
}
}
+ if (options.generateProguardRules) {
+ proguard::collectProguardRulesForManifest(options.manifest, merger.getMergedXml(),
+ keepSet);
+ }
+
BigBuffer outBuffer(1024);
if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
resolver, {}, &outBuffer)) {
@@ -805,8 +819,10 @@
return false;
}
+ proguard::KeepSet keepSet;
+
android::ResTable binTable;
- if (!compileManifest(options, resolver, apkFiles, binTable, &outApk)) {
+ if (!compileManifest(options, resolver, apkFiles, binTable, &outApk, &keepSet)) {
return false;
}
@@ -826,7 +842,7 @@
assert(uncompressedData);
if (!linkXml(options, outTable, resolver, item, uncompressedData,
- entry->getUncompressedLen(), &outApk, &linkQueue)) {
+ entry->getUncompressedLen(), &outApk, &linkQueue, &keepSet)) {
Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
<< std::endl;
return false;
@@ -883,6 +899,26 @@
}
}
+ // Generate the Proguard rules file.
+ if (options.generateProguardRules) {
+ const Source& outPath = options.generateProguardRules.value();
+
+ if (options.verbose) {
+ Logger::note(outPath) << "writing proguard rules." << std::endl;
+ }
+
+ std::ofstream fout(outPath.path);
+ if (!fout) {
+ Logger::error(outPath) << strerror(errno) << std::endl;
+ return false;
+ }
+
+ if (!proguard::writeKeepSet(&fout, keepSet)) {
+ Logger::error(outPath) << "failed to write proguard rules." << std::endl;
+ return false;
+ }
+ }
+
outTable->getValueStringPool().prune();
outTable->getValueStringPool().sort(
[](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
@@ -1072,6 +1108,11 @@
options.generateJavaClass = Source{ arg.toString() };
});
+ flag::optionalFlag("--proguard", "file in which to output proguard rules",
+ [&options](const StringPiece& arg) {
+ options.generateProguardRules = Source{ arg.toString() };
+ });
+
flag::optionalSwitch("--static-lib", "generate a static Android library", true,
&isStaticLib);
diff --git a/tools/aapt2/ProguardRules.cpp b/tools/aapt2/ProguardRules.cpp
new file mode 100644
index 0000000..e89fb7c
--- /dev/null
+++ b/tools/aapt2/ProguardRules.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#include "ProguardRules.h"
+#include "Util.h"
+#include "XmlDom.h"
+
+#include <memory>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+class BaseVisitor : public xml::Visitor {
+public:
+ BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
+ }
+
+ virtual void visit(xml::Text*) override {};
+
+ virtual void visit(xml::Namespace* node) override {
+ for (const auto& child : node->children) {
+ child->accept(this);
+ }
+ }
+
+ virtual void visit(xml::Element* node) override {
+ if (!node->namespaceUri.empty()) {
+ Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
+ node->namespaceUri);
+ if (maybePackage) {
+ // This is a custom view, let's figure out the class name from this.
+ std::u16string package = maybePackage.value() + u"." + node->name;
+ if (util::isJavaClassName(package)) {
+ addClass(node->lineNumber, package);
+ }
+ }
+ } else if (util::isJavaClassName(node->name)) {
+ addClass(node->lineNumber, node->name);
+ }
+
+ for (const auto& child: node->children) {
+ child->accept(this);
+ }
+ }
+
+protected:
+ void addClass(size_t lineNumber, const std::u16string& className) {
+ mKeepSet->addClass(mSource.line(lineNumber), className);
+ }
+
+ void addMethod(size_t lineNumber, const std::u16string& methodName) {
+ mKeepSet->addMethod(mSource.line(lineNumber), methodName);
+ }
+
+private:
+ Source mSource;
+ KeepSet* mKeepSet;
+};
+
+struct LayoutVisitor : public BaseVisitor {
+ LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkClass = false;
+ bool checkName = false;
+ if (node->namespaceUri.empty()) {
+ checkClass = node->name == u"view" || node->name == u"fragment";
+ } else if (node->namespaceUri == kSchemaAndroid) {
+ checkName = node->name == u"fragment";
+ }
+
+ for (const auto& attr : node->attributes) {
+ if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
+ util::isJavaClassName(attr.value)) {
+ addClass(node->lineNumber, attr.value);
+ } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
+ util::isJavaClassName(attr.value)) {
+ addClass(node->lineNumber, attr.value);
+ } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
+ addMethod(node->lineNumber, attr.value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct XmlResourceVisitor : public BaseVisitor {
+ XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkFragment = false;
+ if (node->namespaceUri.empty()) {
+ checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
+ }
+
+ if (checkFragment) {
+ xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
+ if (attr && util::isJavaClassName(attr->value)) {
+ addClass(node->lineNumber, attr->value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct TransitionVisitor : public BaseVisitor {
+ TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ bool checkClass = node->namespaceUri.empty() &&
+ (node->name == u"transition" || node->name == u"pathMotion");
+ if (checkClass) {
+ xml::Attribute* attr = node->findAttribute({}, u"class");
+ if (attr && util::isJavaClassName(attr->value)) {
+ addClass(node->lineNumber, attr->value);
+ }
+ }
+
+ BaseVisitor::visit(node);
+ }
+};
+
+struct ManifestVisitor : public BaseVisitor {
+ ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+ }
+
+ virtual void visit(xml::Element* node) override {
+ if (node->namespaceUri.empty()) {
+ bool getName = false;
+ if (node->name == u"manifest") {
+ xml::Attribute* attr = node->findAttribute({}, u"package");
+ if (attr) {
+ mPackage = attr->value;
+ }
+ } else if (node->name == u"application") {
+ getName = true;
+ xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
+ if (attr) {
+ Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+ attr->value);
+ if (result) {
+ addClass(node->lineNumber, result.value());
+ }
+ }
+ } else if (node->name == u"activity" || node->name == u"service" ||
+ node->name == u"receiver" || node->name == u"provider" ||
+ node->name == u"instrumentation") {
+ getName = true;
+ }
+
+ if (getName) {
+ xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
+ if (attr) {
+ Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+ attr->value);
+ if (result) {
+ addClass(node->lineNumber, result.value());
+ }
+ }
+ }
+ }
+ BaseVisitor::visit(node);
+ }
+
+ std::u16string mPackage;
+};
+
+bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet) {
+ ManifestVisitor visitor(source, keepSet);
+ node->accept(&visitor);
+ return true;
+}
+
+bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
+ KeepSet* keepSet) {
+ switch (type) {
+ case ResourceType::kLayout: {
+ LayoutVisitor visitor(source, keepSet);
+ node->accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kXml: {
+ XmlResourceVisitor visitor(source, keepSet);
+ node->accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kTransition: {
+ TransitionVisitor visitor(source, keepSet);
+ node->accept(&visitor);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
+ for (const auto& entry : keepSet.mKeepSet) {
+ for (const SourceLine& source : entry.second) {
+ *out << "// Referenced at " << source << "\n";
+ }
+ *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ }
+
+ for (const auto& entry : keepSet.mKeepMethodSet) {
+ for (const SourceLine& source : entry.second) {
+ *out << "// Referenced at " << source << "\n";
+ }
+ *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+ }
+ return true;
+}
+
+} // namespace proguard
+} // namespace aapt
diff --git a/tools/aapt2/ProguardRules.h b/tools/aapt2/ProguardRules.h
new file mode 100644
index 0000000..bbb3e64
--- /dev/null
+++ b/tools/aapt2/ProguardRules.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_PROGUARD_RULES_H
+#define AAPT_PROGUARD_RULES_H
+
+#include "Resource.h"
+#include "Source.h"
+#include "XmlDom.h"
+
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+class KeepSet {
+public:
+ inline void addClass(const SourceLine& source, const std::u16string& className) {
+ mKeepSet[className].insert(source);
+ }
+
+ inline void addMethod(const SourceLine& source, const std::u16string& methodName) {
+ mKeepMethodSet[methodName].insert(source);
+ }
+
+private:
+ friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+ std::map<std::u16string, std::set<SourceLine>> mKeepSet;
+ std::map<std::u16string, std::set<SourceLine>> mKeepMethodSet;
+};
+
+bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet);
+bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
+ KeepSet* keepSet);
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+} // namespace proguard
+} // namespace aapt
+
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 10c75aa..3606488 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -19,6 +19,7 @@
#include <ostream>
#include <string>
+#include <tuple>
namespace aapt {
@@ -80,6 +81,10 @@
return out << source.path << ":" << source.line << ":" << source.column;
}
+inline bool operator<(const SourceLine& lhs, const SourceLine& rhs) {
+ return std::tie(lhs.path, lhs.line) < std::tie(rhs.path, rhs.line);
+}
+
} // namespace aapt
#endif // AAPT_SOURCE_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
index 7adaf1e..03ecd1ac 100644
--- a/tools/aapt2/Util.cpp
+++ b/tools/aapt2/Util.cpp
@@ -102,6 +102,51 @@
return endIter;
}
+bool isJavaClassName(const StringPiece16& str) {
+ size_t pieces = 0;
+ for (const StringPiece16& piece : tokenize(str, u'.')) {
+ pieces++;
+ if (piece.empty()) {
+ return false;
+ }
+
+ // Can't have starting or trailing $ character.
+ if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') {
+ return false;
+ }
+
+ if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) {
+ return false;
+ }
+ }
+ return pieces >= 2;
+}
+
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+ const StringPiece16& className) {
+ if (className.empty()) {
+ return {};
+ }
+
+ if (util::isJavaClassName(className)) {
+ return className.toString();
+ }
+
+ if (package.empty()) {
+ return {};
+ }
+
+ std::u16string result(package.data(), package.size());
+ if (className.data()[0] != u'.') {
+ result += u'.';
+ }
+ result.append(className.data(), className.size());
+ if (!isJavaClassName(result)) {
+ return {};
+ }
+ return result;
+}
+
static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
char16_t code = 0;
for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
index 6015d825..9cdb152 100644
--- a/tools/aapt2/Util.h
+++ b/tools/aapt2/Util.h
@@ -78,6 +78,23 @@
const StringPiece16& allowedChars);
/**
+ * Tests that the string is a valid Java class name.
+ */
+bool isJavaClassName(const StringPiece16& str);
+
+/**
+ * Converts the class name to a fully qualified class name from the given `package`. Ex:
+ *
+ * asdf --> package.asdf
+ * .asdf --> package.asdf
+ * .a.b --> package.a.b
+ * asdf.adsf --> asdf.adsf
+ */
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+ const StringPiece16& className);
+
+
+/**
* Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
* This will be present in C++14 and can be removed then.
*/
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
index c16f6bb..0b08d24 100644
--- a/tools/aapt2/Util_test.cpp
+++ b/tools/aapt2/Util_test.cpp
@@ -93,4 +93,44 @@
ASSERT_EQ(tokenizer.end(), iter);
}
+TEST(UtilTest, IsJavaClassName) {
+ EXPECT_TRUE(util::isJavaClassName(u"android.test.Class"));
+ EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner"));
+ EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class"));
+ EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_"));
+ EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner"));
+ EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$"));
+ EXPECT_FALSE(util::isJavaClassName(u".test.Class"));
+ EXPECT_FALSE(util::isJavaClassName(u"android"));
+}
+
+TEST(UtilTest, FullyQualifiedClassName) {
+ Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
+ ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.asdf");
+
+ res = util::getFullyQualifiedClassName(u"android", u".asdf");
+ ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.asdf");
+
+ res = util::getFullyQualifiedClassName(u"android", u".a.b");
+ ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"android.a.b");
+
+ res = util::getFullyQualifiedClassName(u"android", u"a.b");
+ ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"a.b");
+
+ res = util::getFullyQualifiedClassName(u"", u"a.b");
+ ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), u"a.b");
+
+ res = util::getFullyQualifiedClassName(u"", u"");
+ ASSERT_FALSE(res);
+
+ res = util::getFullyQualifiedClassName(u"android", u"./Apple");
+ ASSERT_FALSE(res);
+}
+
+
} // namespace aapt
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
index c017a0d..8533c28 100644
--- a/tools/aapt2/data/AndroidManifest.xml
+++ b/tools/aapt2/data/AndroidManifest.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app">
- <application>
+ <application
+ android:name=".Activity">
</application>
</manifest>
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
index ce5201b..3387135 100644
--- a/tools/aapt2/data/Makefile
+++ b/tools/aapt2/data/Makefile
@@ -15,6 +15,7 @@
LOCAL_LIBS := lib/out/package.apk
LOCAL_OUT := out
LOCAL_GEN := out/gen
+LOCAL_PROGUARD := out/proguard.rule
##
# AAPT2 custom rules.
@@ -57,7 +58,7 @@
# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_INCLUDES) $(LOCAL_LIBS) AndroidManifest.xml
- $(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS)
+ $(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS) --proguard $(LOCAL_PROGUARD) -v
# R.java: gen/com/android/app/R.java <- out/resources.arsc
# No action since R.java is generated when out/resources.arsc is.
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
index 77ccedb..50a51d9 100644
--- a/tools/aapt2/data/res/layout/main.xml
+++ b/tools/aapt2/data/res/layout/main.xml
@@ -5,11 +5,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <fragment class="android.test.sample.App$Inner" />
+
<variable name="user" type="com.android.User" />
<View xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/me"
android:layout_width="1dp"
+ android:onClick="doClick"
android:text="@{user.name}"
android:layout_height="match_parent"
app:layout_width="@support:bool/allow"