diff options
Diffstat (limited to 'core/java/android')
89 files changed, 2119 insertions, 890 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index dd5f18e..f6ad847 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1095,7 +1095,8 @@ public final class AnimatorSet extends Animator { public Node clone() { try { Node node = (Node) super.clone(); - node.animation = (Animator) animation.clone(); + node.animation = animation.clone(); + node.done = false; return node; } catch (CloneNotSupportedException e) { throw new AssertionError(); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 275e78e..292507b 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -1486,6 +1486,11 @@ public class ValueAnimator extends Animator { anim.mPaused = false; anim.mResumed = false; anim.mStartListenersCalled = false; + anim.mStartTime = 0; + anim.mStartTimeCommitted = false; + anim.mPauseTime = 0; + anim.mCurrentFraction = 0; + anim.mDelayStartTime = 0; PropertyValuesHolder[] oldValues = mValues; if (oldValues != null) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 51ececc..576a046 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -86,6 +86,13 @@ public class ActivityManager { public static final String META_HOME_ALTERNATE = "android.app.home.alternate"; /** + * Result for IActivityManager.startActivity: trying to start a background user + * activity that shouldn't be displayed for all users. + * @hide + */ + public static final int START_NOT_CURRENT_USER_ACTIVITY = -8; + + /** * Result for IActivityManager.startActivity: trying to start an activity under voice * control when that activity does not support the VOICE category. * @hide @@ -862,6 +869,23 @@ public class ActivityManager { */ public int affiliatedTaskColor; + /** + * The component launched as the first activity in the task. + * This can be considered the "application" of this task. + */ + public ComponentName baseActivity; + + /** + * The activity component at the top of the history stack of the task. + * This is what the user is currently doing. + */ + public ComponentName topActivity; + + /** + * Number of activities in this task. + */ + public int numActivities; + public RecentTaskInfo() { } @@ -895,6 +919,9 @@ public class ActivityManager { dest.writeLong(lastActiveTime); dest.writeInt(affiliatedTaskId); dest.writeInt(affiliatedTaskColor); + ComponentName.writeToParcel(baseActivity, dest); + ComponentName.writeToParcel(topActivity, dest); + dest.writeInt(numActivities); } public void readFromParcel(Parcel source) { @@ -911,6 +938,9 @@ public class ActivityManager { lastActiveTime = source.readLong(); affiliatedTaskId = source.readInt(); affiliatedTaskColor = source.readInt(); + baseActivity = ComponentName.readFromParcel(source); + topActivity = ComponentName.readFromParcel(source); + numActivities = source.readInt(); } public static final Creator<RecentTaskInfo> CREATOR diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d56dc1e..bde8f39 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.NonNull; + /** * Activity manager local system service interface. * @@ -27,4 +29,23 @@ public abstract class ActivityManagerInternal { public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs, String processName, String abiOverride, int uid, Runnable crashHandler); + + /** + * Acquires a sleep token with the specified tag. + * + * @param tag A string identifying the purpose of the token (eg. "Dream"). + */ + public abstract SleepToken acquireSleepToken(@NonNull String tag); + + /** + * Sleep tokens cause the activity manager to put the top activity to sleep. + * They are used by components such as dreams that may hide and block interaction + * with underlying activities. + */ + public static abstract class SleepToken { + /** + * Releases the sleep token. + */ + public abstract void release(); + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 256d87d..add7af2 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -93,15 +93,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } static boolean sSystemReady = false; + static public void broadcastStickyIntent(Intent intent, String permission, int userId) { + broadcastStickyIntent(intent, permission, AppOpsManager.OP_NONE, userId); + } + /** * Convenience for sending a sticky broadcast. For internal use only. * If you don't care about permission, use null. */ - static public void broadcastStickyIntent(Intent intent, String permission, int userId) { + static public void broadcastStickyIntent(Intent intent, String permission, int appOp, + int userId) { try { getDefault().broadcastIntent( null, intent, null, null, Activity.RESULT_OK, null, null, - null /*permission*/, AppOpsManager.OP_NONE, false, true, userId); + null /*permission*/, appOp, false, true, userId); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 381c20c..223d528 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.annotation.SystemApi; import android.app.usage.UsageStatsManager; import android.content.Context; @@ -212,8 +213,12 @@ public class AppOpsManager { public static final int OP_ASSIST_STRUCTURE = 49; /** @hide Received a screenshot from assist. */ public static final int OP_ASSIST_SCREENSHOT = 50; + /** @hide Read the phone state. */ + public static final int OP_READ_PHONE_STATE = 51; + /** @hide Add voicemail messages to the voicemail content provider. */ + public static final int OP_ADD_VOICEMAIL = 52; /** @hide */ - public static final int _NUM_OP = 51; + public static final int _NUM_OP = 53; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -294,6 +299,8 @@ public class AppOpsManager { OP_WRITE_WALLPAPER, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT, + OP_READ_PHONE_STATE, + OP_ADD_VOICEMAIL }; /** @@ -352,6 +359,8 @@ public class AppOpsManager { null, null, null, + null, + null }; /** @@ -409,7 +418,9 @@ public class AppOpsManager { "ACTIVATE_VPN", "WRITE_WALLPAPER", "ASSIST_STRUCTURE", - "ASSIST_SCREENSHOT" + "ASSIST_SCREENSHOT", + "OP_READ_PHONE_STATE", + "ADD_VOICEMAIL" }; /** @@ -432,14 +443,14 @@ public class AppOpsManager { null, // neighboring cells shares the coarse location perm android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_SMS, - android.Manifest.permission.WRITE_SMS, + null, // no permission required for writing sms android.Manifest.permission.RECEIVE_SMS, android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST, android.Manifest.permission.RECEIVE_MMS, android.Manifest.permission.RECEIVE_WAP_PUSH, android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_SMS, - android.Manifest.permission.WRITE_SMS, + null, // no permission required for writing icc sms android.Manifest.permission.WRITE_SETTINGS, android.Manifest.permission.SYSTEM_ALERT_WINDOW, android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -468,6 +479,8 @@ public class AppOpsManager { null, // no permission for supporting wallpaper null, // no permission for receiving assist structure null, // no permission for receiving assist screenshot + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ADD_VOICEMAIL }; /** @@ -527,6 +540,8 @@ public class AppOpsManager { UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER null, // ASSIST_STRUCTURE null, // ASSIST_SCREENSHOT + null, // READ_PHONE_STATE + null // ADD_VOICEMAIL }; /** @@ -585,6 +600,8 @@ public class AppOpsManager { false, //WALLPAPER false, //ASSIST_STRUCTURE false, //ASSIST_SCREENSHOT + false, //READ_PHONE_STATE + false //ADD_VOICEMAIL }; /** @@ -642,6 +659,8 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED }; /** @@ -703,6 +722,8 @@ public class AppOpsManager { false, false, false, + false, + false }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index b9ddff0..dfe7e18 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1520,19 +1520,21 @@ final class ApplicationPackageManager extends PackageManager { // Should never happen! } } + @Override - public void freeStorageAndNotify(long idealStorageSize, IPackageDataObserver observer) { + public void freeStorageAndNotify(String volumeUuid, long idealStorageSize, + IPackageDataObserver observer) { try { - mPM.freeStorageAndNotify(idealStorageSize, observer); + mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, observer); } catch (RemoteException e) { // Should never happen! } } @Override - public void freeStorage(long freeStorageSize, IntentSender pi) { + public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) { try { - mPM.freeStorage(freeStorageSize, pi); + mPM.freeStorage(volumeUuid, freeStorageSize, pi); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4ccd69f..81a78f6 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -860,13 +860,19 @@ class ContextImpl extends Context { @Override public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) { + sendBroadcastAsUser(intent, user, receiverPermission, AppOpsManager.OP_NONE); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( - mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false, - user.getIdentifier()); + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, + user.getIdentifier()); } catch (RemoteException e) { } } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 913159a..e275df0 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -89,4 +89,9 @@ interface INotificationManager boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); + + byte[] getBackupPayload(int user); + void applyRestore(in byte[] payload, int user); + + ParceledListSlice getAppActiveNotifications(String callingPkg, int userId); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index fc96464..b77dec5 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1797,6 +1797,10 @@ public class Instrumentation { case ActivityManager.START_NOT_VOICE_COMPATIBLE: throw new SecurityException( "Starting under voice control not allowed for: " + intent); + case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY: + throw new SecurityException( + "Not allowed to start background user activity that shouldn't be" + + " displayed for all users."); default: throw new AndroidRuntimeException("Unknown error code " + res + " when starting " + intent); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 7133dce..0a59026 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -23,6 +23,7 @@ import android.app.Notification.Builder; import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -35,10 +36,12 @@ import android.os.StrictMode; import android.os.UserHandle; import android.provider.Settings.Global; import android.service.notification.IConditionListener; +import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.util.Log; import java.util.Objects; +import java.util.List; /** * Class to notify the user of events that happen. This is how you tell @@ -642,4 +645,30 @@ public class NotificationManager } } + /** + * Recover a list of active notifications: ones that have been posted by the calling app that + * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app. + * + * Each notification is embedded in a {@link StatusBarNotification} object, including the + * original <code>tag</code> and <code>id</code> supplied to + * {@link #notify(String, int, Notification) notify()} + * (via {@link StatusBarNotification#getTag() getTag()} and + * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original + * {@link Notification} object (via {@link StatusBarNotification#getNotification()}). + * + * @return An array of {@link StatusBarNotification}. + */ + public StatusBarNotification[] getActiveNotifications() { + final INotificationManager service = getService(); + final String pkg = mContext.getPackageName(); + try { + final ParceledListSlice<StatusBarNotification> parceledList + = service.getAppActiveNotifications(pkg, UserHandle.myUserId()); + final List<StatusBarNotification> list = parceledList.getList(); + return list.toArray(new StatusBarNotification[list.size()]); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + return new StatusBarNotification[0]; + } } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index c719a0e..b1a5d21 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -643,6 +643,10 @@ public class SearchDialog extends Dialog { @Override public ActionMode startActionModeForChild( View child, ActionMode.Callback callback, int type) { + // Disable Primary Action Modes in the SearchBar, as they overlap. + if (type != ActionMode.TYPE_PRIMARY) { + return super.startActionModeForChild(child, callback, type); + } return null; } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index e446700..46da025 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -332,10 +332,10 @@ final class SystemServiceRegistry { }}); registerService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, - new StaticServiceFetcher<NetworkPolicyManager>() { + new CachedServiceFetcher<NetworkPolicyManager>() { @Override - public NetworkPolicyManager createService() { - return new NetworkPolicyManager(INetworkPolicyManager.Stub.asInterface( + public NetworkPolicyManager createService(ContextImpl ctx) { + return new NetworkPolicyManager(ctx, INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE))); }}); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 44760ce..9e2da61 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -364,9 +364,12 @@ public class DevicePolicyManager { /** * A String extra holding the URL-safe base64 encoded SHA-1 checksum of the file at download - * location specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. If - * this doesn't match the file at the download location an error will be shown to the user and - * the user will be asked to factory reset the device. + * location specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. + * + * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_CERTIFICATE_CHECKSUM} should be + * present. The provided checksum should match the checksum of the file at the download + * location. If the checksum doesn't match an error will be shown to the user and the user will + * be asked to factory reset the device. * * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner * provisioning via an NFC bump. @@ -375,6 +378,26 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; /** + * A String extra holding the URL-safe base64 encoded SHA-1 checksum of any certificate of the + * android package archive at the download location specified in {@link + * #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. + * + * <p>The certificates of an android package archive can be obtained using + * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag + * {@link android.content.pm.PackageManager#GET_SIGNATURES}. + * + * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM} should be + * present. The provided checksum should match the checksum of any certificate of the file at + * the download location. If the checksum does not match an error will be shown to the user and + * the user will be asked to factory reset the device. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_CERTIFICATE_CHECKSUM + = "android.app.extra.PROVISIONING_DEVICE_ADMIN_CERTIFICATE_CHECKSUM"; + + /** * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile * has completed successfully. * @@ -449,9 +472,12 @@ public class DevicePolicyManager { /** * A String extra holding the URL-safe base64 encoded SHA-1 checksum of the file at download * location specified in - * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. If this doesn't - * match the file at the download location an error will be shown to the user and the user will - * be asked to factory reset the device. + * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. + * + * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_CERTIFICATE_CHECKSUM} + * should be present. The provided checksum should match the checksum of the file at the + * download location. If the checksum doesn't match an error will be shown to the user and the + * user will be asked to factory reset the device. * * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner * provisioning via an NFC bump. @@ -460,6 +486,26 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM"; /** + * A String extra holding the URL-safe base64 encoded SHA-1 checksum of any certificate of the + * android package archive at the download location specified in {@link + * #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. + * + * <p>The certificates of an android package archive can be obtained using + * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag + * {@link android.content.pm.PackageManager#GET_SIGNATURES}. + * + * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM} + * should be present. The provided checksum should match the checksum of any certificate of the + * file at the download location. If the checksum doesn't match an error will be shown to the + * user and the user will be asked to factory reset the device. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_CERTIFICATE_CHECKSUM + = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_CERTIFICATE_CHECKSUM"; + + /** * A String extra holding the MAC address of the Bluetooth device to connect to with status * updates during provisioning. * @@ -754,11 +800,12 @@ public class DevicePolicyManager { public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** - * Broadcast action: notify that a new local OTA policy has been set by the device owner. - * The new policy can be retrieved by {@link #getOtaPolicy()}. + * Broadcast action: notify that a new local system update policy has been set by the device + * owner. The new policy can be retrieved by {@link #getSystemUpdatePolicy()}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_OTA_POLICY_CHANGED = "android.app.action.OTA_POLICY_CHANGED"; + public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED + = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; /** @@ -1588,6 +1635,23 @@ public class DevicePolicyManager { } /** + * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. + * + * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. + * @hide + */ + public boolean getDoNotAskCredentialsOnBoot() { + if (mService != null) { + try { + return mService.getDoNotAskCredentialsOnBoot(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call getDoNotAskCredentialsOnBoot()", e); + } + } + return false; + } + + /** * Setting this to a value greater than zero enables a built-in policy * that will perform a device wipe after too many incorrect * device-unlock passwords have been entered. This built-in policy combines @@ -1664,6 +1728,16 @@ public class DevicePolicyManager { public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001; /** + * Flag for {@link #resetPassword}: don't ask for user credentials on device boot. + * If the flag is set, the device can be booted without asking for user password. + * The absence of this flag does not change the current boot requirements. This flag + * can be set by the device owner only. If the app is not the device owner, the flag + * is ignored. Once the flag is set, it cannot be reverted back without resetting the + * device to factory defaults. + */ + public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; + + /** * Force a new device unlock password (the password needed to access the * entire device, not for individual accounts) on the user. This takes * effect immediately. @@ -1686,7 +1760,8 @@ public class DevicePolicyManager { * <p>Calling this from a managed profile will throw a security exception. * * @param password The new password for the user. Null or empty clears the password. - * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}. + * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and + * {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}. * @return Returns true if the password was applied, or false if it is * not acceptable for the current constraints. */ @@ -4143,46 +4218,46 @@ public class DevicePolicyManager { } } - /* - * Called by device owners to set a local OTA update policy. When a new OTA policy is set, - * {@link #ACTION_OTA_POLICY_CHANGED} is broadcasted. + /** + * Called by device owners to set a local system update policy. When a new policy is set, + * {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcasted. * * @param who Which {@link DeviceAdminReceiver} this request is associated with. All components - * in the device owner package can set OTA policies and the most recent policy takes effect. - * @param policy the new OTA policy, or null to clear the current policy. - * @see OtaPolicy + * in the device owner package can set system update policies and the most recent policy takes + * effect. + * @param policy the new policy, or null to clear the current policy. + * @see SystemUpdatePolicy */ - public void setOtaPolicy(ComponentName who, OtaPolicy policy) { + public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (mService != null) { try { if (policy != null) { - mService.setOtaPolicy(who, policy.getPolicyBundle()); + mService.setSystemUpdatePolicy(who, policy.getPolicyBundle()); } else { - mService.setOtaPolicy(who, null); + mService.setSystemUpdatePolicy(who, null); } } catch (RemoteException re) { - Log.w(TAG, "Error calling setOtaPolicy", re); + Log.w(TAG, "Error calling setSystemUpdatePolicy", re); } } } /** - * Retrieve a local OTA update policy set previously by {@link #setOtaPolicy}. + * Retrieve a local system update policy set previously by {@link #setSystemUpdatePolicy}. * - * @return The current OTA policy object, or null if no policy is set or the system does not - * support managed OTA. + * @return The current policy object, or null if no policy is set. */ - public OtaPolicy getOtaPolicy() { + public SystemUpdatePolicy getSystemUpdatePolicy() { if (mService != null) { try { - PersistableBundle bundle = mService.getOtaPolicy(); + PersistableBundle bundle = mService.getSystemUpdatePolicy(); if (bundle != null) { - return new OtaPolicy(bundle); + return new SystemUpdatePolicy(bundle); } else { return null; } } catch (RemoteException re) { - Log.w(TAG, "Error calling getOtaPolicy", re); + Log.w(TAG, "Error calling getSystemUpdatePolicy", re); } } return null; diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7502e1d..1f7498e 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -219,9 +219,10 @@ interface IDevicePolicyManager { void setUserIcon(in ComponentName admin, in Bitmap icon); void sendDeviceInitializerStatus(int statusCode, String description); - void setOtaPolicy(in ComponentName who, in PersistableBundle policy); - PersistableBundle getOtaPolicy(); + void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy); + PersistableBundle getSystemUpdatePolicy(); boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled); void setStatusBarEnabledState(in ComponentName who, boolean enabled); + boolean getDoNotAskCredentialsOnBoot(); } diff --git a/core/java/android/app/admin/OtaPolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index 98581a7..de56cd0 100644 --- a/core/java/android/app/admin/OtaPolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -23,12 +23,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * A class that represents a local OTA policy set by the device owner. + * A class that represents a local system update policy set by the device owner. * - * @see DevicePolicyManager#setOtaPolicy - * @see DevicePolicyManager#getOtaPolicy + * @see DevicePolicyManager#setSystemUpdatePolicy + * @see DevicePolicyManager#getSystemUpdatePolicy */ -public class OtaPolicy { +public class SystemUpdatePolicy { /** @hide */ @IntDef({ @@ -36,22 +36,27 @@ public class OtaPolicy { TYPE_INSTALL_WINDOWED, TYPE_POSTPONE}) @Retention(RetentionPolicy.SOURCE) - @interface OtaPolicyType {} + @interface SystemUpdatePolicyType {} /** - * Install OTA update automatically as soon as one is available. + * Install system update automatically as soon as one is available. */ public static final int TYPE_INSTALL_AUTOMATIC = 1; /** - * Install OTA update automatically within a daily maintenance window, for a maximum of two-week - * period. After that period the OTA will be installed automatically. + * Install system update automatically within a daily maintenance window, for a maximum of 30 + * days. After the expiration the policy will no longer be effective and the system should + * revert back to its normal behavior as if no policy were set. The only exception is + * {@link #TYPE_INSTALL_AUTOMATIC} which should still take effect to install system update + * immediately. */ public static final int TYPE_INSTALL_WINDOWED = 2; /** - * Incoming OTA will be blocked for a maximum of two weeks, after which it will be installed - * automatically. + * Incoming system update will be blocked for a maximum of 30 days, after which the system + * should revert back to its normal behavior as if no policy were set. The only exception is + * {@link #TYPE_INSTALL_AUTOMATIC} which should still take effect to install system update + * immediately. */ public static final int TYPE_POSTPONE = 3; @@ -61,15 +66,15 @@ public class OtaPolicy { private PersistableBundle mPolicy; - public OtaPolicy() { + public SystemUpdatePolicy() { mPolicy = new PersistableBundle(); } /** - * Construct an OtaPolicy object from a bundle. + * Construct an SystemUpdatePolicy object from a bundle. * @hide */ - public OtaPolicy(PersistableBundle in) { + public SystemUpdatePolicy(PersistableBundle in) { mPolicy = new PersistableBundle(in); } @@ -82,7 +87,9 @@ public class OtaPolicy { } /** - * Set the OTA policy to: install OTA update automatically as soon as one is available. + * Set the policy to: install update automatically as soon as one is available. + * + * @see #TYPE_INSTALL_AUTOMATIC */ public void setAutomaticInstallPolicy() { mPolicy.clear(); @@ -90,18 +97,19 @@ public class OtaPolicy { } /** - * Set the OTA policy to: new OTA update will only be installed automatically when the system + * Set the policy to: new system update will only be installed automatically when the system * clock is inside a daily maintenance window. If the start and end times are the same, the - * window is considered to include the WHOLE 24 hours, that is, OTAs can install at any time. If - * the given window in invalid, a {@link OtaPolicy.InvalidWindowException} will be thrown. If - * start time is later than end time, the window is considered spanning midnight, i.e. end time - * donates a time on the next day. The maintenance window will last for two weeks, after which - * the OTA will be installed automatically. + * window is considered to include the WHOLE 24 hours, that is, updates can install at any time. + * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be + * thrown. If start time is later than end time, the window is considered spanning midnight, + * i.e. end time donates a time on the next day. The maintenance window will last for 30 days, + * after which the system should revert back to its normal behavior as if no policy were set. * * @param startTime the start of the maintenance window, measured as the number of minutes from - * midnight in the device's local time. Must be in the range of [0, 1440). + * midnight in the device's local time. Must be in the range of [0, 1440). * @param endTime the end of the maintenance window, measured as the number of minutes from - * midnight in the device's local time. Must be in the range of [0, 1440). + * midnight in the device's local time. Must be in the range of [0, 1440). + * @see #TYPE_INSTALL_WINDOWED */ public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { @@ -114,8 +122,10 @@ public class OtaPolicy { } /** - * Set the OTA policy to: block installation for a maximum period of two weeks. After the - * block expires the OTA will be installed automatically. + * Set the policy to: block installation for a maximum period of 30 days. After expiration the + * system should revert back to its normal behavior as if no policy were set. + * + * @see #TYPE_POSTPONE */ public void setPostponeInstallPolicy() { mPolicy.clear(); @@ -123,12 +133,12 @@ public class OtaPolicy { } /** - * Returns the type of OTA policy. + * Returns the type of system update policy. * * @return an integer, either one of {@link #TYPE_INSTALL_AUTOMATIC}, * {@link #TYPE_INSTALL_WINDOWED} and {@link #TYPE_POSTPONE}, or -1 if no policy has been set. */ - @OtaPolicyType + @SystemUpdatePolicyType public int getPolicyType() { return mPolicy.getInt(KEY_POLICY_TYPE, -1); } @@ -167,7 +177,7 @@ public class OtaPolicy { } /** - * Exception thrown by {@link OtaPolicy#setWindowedInstallPolicy(int, int)} in case the + * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the * specified window is invalid. */ public static class InvalidWindowException extends Exception { diff --git a/core/java/android/app/backup/BlobBackupHelper.java b/core/java/android/app/backup/BlobBackupHelper.java new file mode 100644 index 0000000..cdc62dc --- /dev/null +++ b/core/java/android/app/backup/BlobBackupHelper.java @@ -0,0 +1,296 @@ +/* + * 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.app.backup; + +import android.os.ParcelFileDescriptor; +import android.util.ArrayMap; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.CRC32; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Utility class for writing BackupHelpers whose underlying data is a + * fixed set of byte-array blobs. The helper manages diff detection + * and compression on the wire. + * + * @hide + */ +public abstract class BlobBackupHelper implements BackupHelper { + private static final String TAG = "BlobBackupHelper"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final int mCurrentBlobVersion; + private final String[] mKeys; + + public BlobBackupHelper(int currentBlobVersion, String... keys) { + mCurrentBlobVersion = currentBlobVersion; + mKeys = keys; + } + + // Client interface + + /** + * Generate and return the byte array containing the backup payload describing + * the current data state. During a backup operation this method is called once + * per key that was supplied to the helper's constructor. + * + * @return A byte array containing the data blob that the caller wishes to store, + * or {@code null} if the current state is empty or undefined. + */ + abstract protected byte[] getBackupPayload(String key); + + /** + * Given a byte array that was restored from backup, do whatever is appropriate + * to apply that described state in the live system. This method is called once + * per key/value payload that was delivered for restore. Typically data is delivered + * for restore in lexical order by key, <i>not</i> in the order in which the keys + * were supplied in the constructor. + * + * @param payload The byte array that was passed to {@link #getBackupPayload()} + * on the ancestral device. + */ + abstract protected void applyRestoredPayload(String key, byte[] payload); + + + // Internal implementation + + /* + * State on-disk format: + * [Int] : overall blob version number + * [Int=N] : number of keys represented in the state blob + * N* : + * [String] key + * [Long] blob checksum, calculated after compression + */ + @SuppressWarnings("resource") + private ArrayMap<String, Long> readOldState(ParcelFileDescriptor oldStateFd) { + final ArrayMap<String, Long> state = new ArrayMap<String, Long>(); + + FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); + BufferedInputStream bis = new BufferedInputStream(fis); + DataInputStream in = new DataInputStream(bis); + + try { + int version = in.readInt(); + if (version <= mCurrentBlobVersion) { + final int numKeys = in.readInt(); + for (int i = 0; i < numKeys; i++) { + String key = in.readUTF(); + long checksum = in.readLong(); + state.put(key, checksum); + } + } else { + Log.w(TAG, "Prior state from unrecognized version " + version); + } + } catch (EOFException e) { + // Empty file is expected on first backup, so carry on. If the state + // is truncated we just treat it the same way. + state.clear(); + } catch (Exception e) { + Log.e(TAG, "Error examining prior backup state " + e.getMessage()); + state.clear(); + } + + return state; + } + + /** + * New overall state record + */ + private void writeBackupState(ArrayMap<String, Long> state, ParcelFileDescriptor stateFile) { + try { + FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); + + // We explicitly don't close 'out' because we must not close the backing fd. + // The FileOutputStream will not close it implicitly. + @SuppressWarnings("resource") + DataOutputStream out = new DataOutputStream(fos); + + out.writeInt(mCurrentBlobVersion); + + final int N = (state != null) ? state.size() : 0; + out.writeInt(N); + for (int i = 0; i < N; i++) { + out.writeUTF(state.keyAt(i)); + out.writeLong(state.valueAt(i).longValue()); + } + } catch (IOException e) { + Log.e(TAG, "Unable to write updated state", e); + } + } + + // Also versions the deflated blob internally in case we need to revise it + private byte[] deflate(byte[] data) { + byte[] result = null; + if (data != null) { + try { + ByteArrayOutputStream sink = new ByteArrayOutputStream(); + DataOutputStream headerOut = new DataOutputStream(sink); + + // write the header directly to the sink ahead of the deflated payload + headerOut.writeInt(mCurrentBlobVersion); + + DeflaterOutputStream out = new DeflaterOutputStream(sink); + out.write(data); + out.close(); // finishes and commits the compression run + result = sink.toByteArray(); + if (DEBUG) { + Log.v(TAG, "Deflated " + data.length + " bytes to " + result.length); + } + } catch (IOException e) { + Log.w(TAG, "Unable to process payload: " + e.getMessage()); + } + } + return result; + } + + // Returns null if inflation failed + private byte[] inflate(byte[] compressedData) { + byte[] result = null; + if (compressedData != null) { + try { + ByteArrayInputStream source = new ByteArrayInputStream(compressedData); + DataInputStream headerIn = new DataInputStream(source); + int version = headerIn.readInt(); + if (version > mCurrentBlobVersion) { + Log.w(TAG, "Saved payload from unrecognized version " + version); + return null; + } + + InflaterInputStream in = new InflaterInputStream(source); + ByteArrayOutputStream inflated = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int nRead; + while ((nRead = in.read(buffer)) > 0) { + inflated.write(buffer, 0, nRead); + } + in.close(); + inflated.flush(); + result = inflated.toByteArray(); + if (DEBUG) { + Log.v(TAG, "Inflated " + compressedData.length + " bytes to " + result.length); + } + } catch (IOException e) { + // result is still null here + Log.w(TAG, "Unable to process restored payload: " + e.getMessage()); + } + } + return result; + } + + private long checksum(byte[] buffer) { + if (buffer != null) { + try { + CRC32 crc = new CRC32(); + ByteArrayInputStream bis = new ByteArrayInputStream(buffer); + byte[] buf = new byte[4096]; + int nRead = 0; + while ((nRead = bis.read(buf)) >= 0) { + crc.update(buf, 0, nRead); + } + return crc.getValue(); + } catch (Exception e) { + // whoops; fall through with an explicitly bogus checksum + } + } + return -1; + } + + // BackupHelper interface + + @Override + public void performBackup(ParcelFileDescriptor oldStateFd, BackupDataOutput data, + ParcelFileDescriptor newStateFd) { + + final ArrayMap<String, Long> oldState = readOldState(oldStateFd); + final ArrayMap<String, Long> newState = new ArrayMap<String, Long>(); + + try { + for (String key : mKeys) { + final byte[] payload = deflate(getBackupPayload(key)); + final long checksum = checksum(payload); + newState.put(key, checksum); + + Long oldChecksum = oldState.get(key); + if (oldChecksum == null || checksum != oldChecksum) { + if (DEBUG) { + Log.i(TAG, "State has changed for key " + key + ", writing"); + } + if (payload != null) { + data.writeEntityHeader(key, payload.length); + data.writeEntityData(payload, payload.length); + } else { + // state's changed but there's no current payload => delete + data.writeEntityHeader(key, -1); + } + } else { + if (DEBUG) { + Log.i(TAG, "No change under key " + key + " => not writing"); + } + } + } + } catch (Exception e) { + Log.w(TAG, "Unable to record notification state: " + e.getMessage()); + newState.clear(); + } finally { + // Always recommit the state even if nothing changed + writeBackupState(newState, newStateFd); + } + } + + @Override + public void restoreEntity(BackupDataInputStream data) { + final String key = data.getKey(); + try { + // known key? + int which; + for (which = 0; which < mKeys.length; which++) { + if (key.equals(mKeys[which])) { + break; + } + } + if (which >= mKeys.length) { + Log.e(TAG, "Unrecognized key " + key + ", ignoring"); + return; + } + + byte[] compressed = new byte[data.size()]; + data.read(compressed); + byte[] payload = inflate(compressed); + applyRestoredPayload(key, payload); + } catch (Exception e) { + Log.e(TAG, "Exception restoring entity " + key + " : " + e.getMessage()); + } + } + + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { + // Just ensure that we do a full backup the first time after a restore + writeBackupState(null, newState); + } +} diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl index d80f58c..506dd12 100644 --- a/core/java/android/app/trust/ITrustListener.aidl +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -22,6 +22,6 @@ package android.app.trust; * {@hide} */ oneway interface ITrustListener { - void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser); + void onTrustChanged(boolean enabled, int userId, int flags); void onTrustManagedChanged(boolean managed, int userId); }
\ No newline at end of file diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 705a144..b5c5317 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -34,7 +34,7 @@ public class TrustManager { private static final int MSG_TRUST_MANAGED_CHANGED = 2; private static final String TAG = "TrustManager"; - private static final String DATA_INITIATED_BY_USER = "initiatedByUser"; + private static final String DATA_FLAGS = "initiatedByUser"; private final ITrustManager mService; private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; @@ -109,11 +109,11 @@ public class TrustManager { try { ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { @Override - public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) { + public void onTrustChanged(boolean enabled, int userId, int flags) { Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, trustListener); - if (initiatedByUser) { - m.getData().putBoolean(DATA_INITIATED_BY_USER, initiatedByUser); + if (flags != 0) { + m.getData().putInt(DATA_FLAGS, flags); } m.sendToTarget(); } @@ -156,11 +156,8 @@ public class TrustManager { public void handleMessage(Message msg) { switch(msg.what) { case MSG_TRUST_CHANGED: - boolean initiatedByUser = msg.peekData() != null && - msg.peekData().getBoolean(DATA_INITIATED_BY_USER); - ((TrustListener)msg.obj).onTrustChanged( - msg.arg1 != 0, msg.arg2, initiatedByUser); - + int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0; + ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags); break; case MSG_TRUST_MANAGED_CHANGED: ((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2); @@ -174,10 +171,11 @@ public class TrustManager { * Reports that the trust state has changed. * @param enabled if true, the system believes the environment to be trusted. * @param userId the user, for which the trust changed. - * @param initiatedByUser indicates that the user has explicitly initiated an action that - * proves the user is about to use the device. + * @param flags flags specified by the trust agent when granting trust. See + * {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int) + * TrustAgentService.grantTrust(CharSequence, long, int)}. */ - void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser); + void onTrustChanged(boolean enabled, int userId, int flags); /** * Reports that whether trust is managed has changed diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 370f61c..3bf3f85 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1608,6 +1608,28 @@ public abstract class Context { public abstract void sendBroadcastAsUser(Intent intent, UserHandle user, @Nullable String receiverPermission); + + /** + * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param appOp The app op associated with the broadcast. + * + * @see #sendBroadcast(Intent, String) + * + * @hide + */ + public abstract void sendBroadcastAsUser(Intent intent, UserHandle user, + @Nullable String receiverPermission, int appOp); + /** * Version of * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)} diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 92f0079..fb9e194 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -444,6 +444,13 @@ public class ContextWrapper extends Context { mBase.sendBroadcastAsUser(intent, user, receiverPermission); } + /** @hide */ + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp) { + mBase.sendBroadcastAsUser(intent, user, receiverPermission, appOp); + } + @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 8d82aa2..16f6b1e 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -223,11 +223,12 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_HARDWARE_ACCELERATED = 0x0200; /** - * Value for {@link #flags}: true when the application can be displayed over the lockscreen - * and consequently over all users' windows. + * Value for {@link #flags}: true when the application can be displayed for all users + * regardless of if the user of the application is the current user. Set from the + * {@link android.R.attr#showForAllUsers} attribute. * @hide */ - public static final int FLAG_SHOW_ON_LOCK_SCREEN = 0x0400; + public static final int FLAG_SHOW_FOR_ALL_USERS = 0x0400; /** * Bit in {@link #flags} corresponding to an immersive activity * that wishes not to be interrupted by notifications. diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index caf069f..6c32873 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -339,8 +339,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use * cleartext network traffic, in which case platform components (e.g., HTTP stacks, - * {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic. - * Third-party libraries are encouraged to honor this flag as well. + * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext + * traffic. Third-party libraries are encouraged to honor this flag as well. + * + * <p>NOTE: {@code WebView} does not honor this flag. + * + * <p>This flag comes from + * {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic + * android:usesCleartextTraffic} of the <application> tag. */ public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c2580c0..447c668 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -328,7 +328,7 @@ interface IPackageManager { * @param observer call back used to notify when * the operation is completed */ - void freeStorageAndNotify(in long freeStorageSize, + void freeStorageAndNotify(in String volumeUuid, in long freeStorageSize, IPackageDataObserver observer); /** @@ -352,7 +352,7 @@ interface IPackageManager { * notify when the operation is completed.May be null * to indicate that no call back is desired. */ - void freeStorage(in long freeStorageSize, + void freeStorage(in String volumeUuid, in long freeStorageSize, in IntentSender pi); /** diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index c81517a..5c21c8e 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -146,6 +146,7 @@ public class LauncherApps { try { activities = mService.getLauncherActivities(packageName, user); } catch (RemoteException re) { + throw new RuntimeException("Failed to call LauncherAppsService"); } if (activities == null) { return Collections.EMPTY_LIST; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a128872..a0cec50 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3798,7 +3798,13 @@ public abstract class PackageManager { * @hide */ // @SystemApi - public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); + public void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) { + freeStorageAndNotify(null, freeStorageSize, observer); + } + + /** {@hide} */ + public abstract void freeStorageAndNotify(String volumeUuid, long freeStorageSize, + IPackageDataObserver observer); /** * Free storage by deleting LRU sorted list of cache files across @@ -3823,7 +3829,12 @@ public abstract class PackageManager { * * @hide */ - public abstract void freeStorage(long freeStorageSize, IntentSender pi); + public void freeStorage(long freeStorageSize, IntentSender pi) { + freeStorage(null, freeStorageSize, pi); + } + + /** {@hide} */ + public abstract void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi); /** * Retrieve the size information for a package. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 40f4e8f..fed9261 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3099,8 +3099,9 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; } - if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)) { - a.info.flags |= ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN; + if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false) + || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) { + a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 88fa339..7ad3a68 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -26,6 +26,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import dalvik.system.CloseGuard; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -224,8 +225,8 @@ public class SystemSensorManager extends SensorManager { * the queues and the listeners. */ private static abstract class BaseEventQueue { - private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ, - float[] scratch, String packageName); + private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak, + MessageQueue msgQ, float[] scratch, String packageName); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); @@ -240,7 +241,8 @@ public class SystemSensorManager extends SensorManager { protected final SystemSensorManager mManager; BaseEventQueue(Looper looper, SystemSensorManager manager) { - nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch, + nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this), + looper.getQueue(), mScratch, manager.mPackageName); mCloseGuard.open("dispose"); mManager = manager; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 8f7aed4..87a1ca9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1030,12 +1030,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity * (depth) in pixel coordinates.</p> * <p><b>Units</b>: - * Pixels in the android.sensor.activeArraySize coordinate + * Pixels in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE */ @PublicKey public static final Key<float[]> LENS_INTRINSIC_CALIBRATION = @@ -1330,6 +1331,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li> + * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -1342,6 +1344,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING + * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT */ @PublicKey public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES = @@ -2542,11 +2545,23 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p> * <p>Each higher level supports everything the lower level supports * in this order: FULL <code>></code> LIMITED <code>></code> LEGACY.</p> + * <p>A HIGH_RESOLUTION device is equivalent to a FULL device, except that:</p> + * <ul> + * <li>At least one output resolution of 8 megapixels or higher in uncompressed YUV is + * supported at <code>>=</code> 20 fps.</li> + * <li>Maximum-size (sensor resolution) uncompressed YUV is supported at <code>>=</code> 10 + * fps.</li> + * <li>For devices that list the RAW capability and support either RAW10 or RAW12 output, + * maximum-resolution RAW10 or RAW12 capture will operate at least at the rate of + * maximum-resolution YUV capture, and at least one supported output resolution of + * 8 megapixels or higher in RAW10 or RAW12 is supported <code>>=</code> 20 fps.</li> + * </ul> * <p><b>Possible values:</b> * <ul> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li> + * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION HIGH_RESOLUTION}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -2564,6 +2579,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY + * @see #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION */ @PublicKey public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = @@ -2694,6 +2710,29 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS = new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + /** + * <p>Indicates whether a capture request may target both a + * DEPTH16 / DEPTH_POINT_CLOUD output, and normal color outputs (such as + * YUV_420_888, JPEG, or RAW) simultaneously.</p> + * <p>If TRUE, including both depth and color outputs in a single + * capture request is not supported. An application must interleave color + * and depth requests. If FALSE, a single request can target both types + * of output.</p> + * <p>Typically, this restriction exists on camera devices that + * need to emit a specific pattern or wavelength of light to + * measure depth values, which causes the color image to be + * corrupted during depth measurement.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + */ + @PublicKey + public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE = + new Key<Boolean>("android.depth.depthIsExclusive", boolean.class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 4a5bd08..e3f1d73 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -303,10 +303,13 @@ public abstract class CameraMetadata<TKey> { * <p>The minimal set of capabilities that every camera * device (regardless of {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}) * supports.</p> - * <p>This capability is listed by all devices, and + * <p>This capability is listed by all normal devices, and * indicates that the camera device has a feature set * that's comparable to the baseline requirements for the * older android.hardware.Camera API.</p> + * <p>Devices with the DEPTH_OUTPUT capability might not list this + * capability, indicating that they support only depth measurement, + * not standard color output.</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES @@ -545,6 +548,11 @@ public abstract class CameraMetadata<TKey> { * {@link CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE android.control.awbLockAvailable} are also guaranteed * to be <code>true</code> so burst capture with these two locks ON * yields consistent image output.</p> + * <p>On a camera device that reports the HIGH_RESOLUTION hardware + * level, meaning the device supports very large capture sizes, + * BURST_CAPTURE means that at least 8-megapixel images can be + * captured at <code>>=</code> 20 fps, and maximum-resolution images can be + * captured at <code>>=</code> 10 fps.</p> * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES * @see CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE @@ -596,6 +604,42 @@ public abstract class CameraMetadata<TKey> { */ public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; + /** + * <p>The camera device can produce depth measurements from its field of view.</p> + * <p>This capability requires the camera device to support the following:</p> + * <ul> + * <li>DEPTH16 is supported as an output format.</li> + * <li>DEPTH_POINT_CLOUD is optionally supported as an output format.</li> + * <li>This camera device, and all camera devices with the same android.lens.info.facing, + * will list the following calibration entries in both CameraCharacteristics and + * CaptureResults:<ul> + * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> + * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> + * <li>android.lens.intrinsicCalibration</li> + * <li>android.lens.radialDistortion</li> + * </ul> + * </li> + * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li> + * <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support + * normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16 + * format.</li> + * </ul> + * <p>Generally, depth output operates at a slower frame rate than standard color capture, + * so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that + * should be accounted for (see + * android.hardware.camera2.StreamConfigurationMap#getOutputStallDuration). On a device + * that supports both depth and color-based output, to enable smooth preview, using a + * repeating burst is recommended, where a depth-output target is only included once + * every N frames, where N is the ratio between preview output rate and depth output + * rate, including depth stall time.</p> + * + * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE + * @see CameraCharacteristics#LENS_POSE_ROTATION + * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + */ + public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; + // // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE // @@ -808,6 +852,13 @@ public abstract class CameraMetadata<TKey> { */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; + /** + * <p>This camera device is capable of supporting advanced imaging applications at full rate, + * and additional high-resolution outputs at lower rates.</p> + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + */ + public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; + // // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY // diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 4134d28..ef5d75c 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2628,12 +2628,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity * (depth) in pixel coordinates.</p> * <p><b>Units</b>: - * Pixels in the android.sensor.activeArraySize coordinate + * Pixels in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE */ @PublicKey public static final Key<float[]> LENS_INTRINSIC_CALIBRATION = diff --git a/core/java/android/hardware/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java index ae3d4a4..525f254 100644 --- a/core/java/android/hardware/fingerprint/FingerprintUtils.java +++ b/core/java/android/hardware/fingerprint/FingerprintUtils.java @@ -17,6 +17,8 @@ package android.hardware.fingerprint; import android.content.ContentResolver; +import android.content.Context; +import android.os.Vibrator; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -35,6 +37,8 @@ public class FingerprintUtils { private static final boolean DEBUG = true; private static final String TAG = "FingerprintUtils"; + private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30}; + private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30}; private static int[] toIntArray(List<Integer> list) { if (list == null) { @@ -104,5 +108,19 @@ class FingerprintUtils { return false; } + public static void vibrateFingerprintError(Context context) { + Vibrator vibrator = context.getSystemService(Vibrator.class); + if (vibrator != null) { + vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1); + } + } + + public static void vibrateFingerprintSuccess(Context context) { + Vibrator vibrator = context.getSystemService(Vibrator.class); + if (vibrator != null) { + vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1); + } + } + }; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 47d5fc6..6870327 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -40,7 +40,6 @@ import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.Log; -import com.android.internal.net.VpnConfig; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.Protocol; @@ -99,16 +98,6 @@ public class ConnectivityManager { public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; /** - * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any - * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}. - * - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String CONNECTIVITY_ACTION_IMMEDIATE = - "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE"; - - /** * The lookup key for a {@link NetworkInfo} object. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * @@ -2517,30 +2506,9 @@ public class ConnectivityManager { * @hide */ public void factoryReset() { - // Turn airplane mode off - setAirplaneMode(false); - - // Untether - for (String tether : getTetheredIfaces()) { - untether(tether); - } - - // Turn VPN off try { - VpnConfig vpnConfig = mService.getVpnConfig(); - if (vpnConfig != null) { - if (vpnConfig.legacy) { - mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN); - } else { - // Prevent this app from initiating VPN connections in the future without - // user intervention. - mService.setVpnPackageAuthorization(false); - - mService.prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN); - } - } + mService.factoryReset(); } catch (RemoteException e) { - // Well, we tried } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 9152f69..efc76b3 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -164,4 +164,6 @@ interface IConnectivityManager boolean addVpnAddress(String address, int prefixLength); boolean removeVpnAddress(String address, int prefixLength); boolean setUnderlyingNetworksForVpn(in Network[] networks); + + void factoryReset(); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 7e92de2..c722fbc 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -45,7 +45,7 @@ interface INetworkPolicyManager { /** Control network policies atomically. */ void setNetworkPolicies(in NetworkPolicy[] policies); - NetworkPolicy[] getNetworkPolicies(); + NetworkPolicy[] getNetworkPolicies(String callingPackage); /** Snooze limit on policy matching given template. */ void snoozeLimit(in NetworkTemplate template); @@ -58,4 +58,6 @@ interface INetworkPolicyManager { NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state); boolean isNetworkMetered(in NetworkState state); + + void factoryReset(String subscriber); } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index a7ffee9..bc03637 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -61,12 +61,14 @@ public class NetworkPolicyManager { */ public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE"; + private final Context mContext; private INetworkPolicyManager mService; - public NetworkPolicyManager(INetworkPolicyManager service) { + public NetworkPolicyManager(Context context, INetworkPolicyManager service) { if (service == null) { throw new IllegalArgumentException("missing INetworkPolicyManager"); } + mContext = context; mService = service; } @@ -158,7 +160,7 @@ public class NetworkPolicyManager { public NetworkPolicy[] getNetworkPolicies() { try { - return mService.getNetworkPolicies(); + return mService.getNetworkPolicies(mContext.getOpPackageName()); } catch (RemoteException e) { return null; } @@ -185,24 +187,9 @@ public class NetworkPolicyManager { * @hide */ public void factoryReset(String subscriber) { - // Turn mobile data limit off - NetworkPolicy[] policies = getNetworkPolicies(); - NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriber); - for (NetworkPolicy policy : policies) { - if (policy.template.equals(template)) { - policy.limitBytes = NetworkPolicy.LIMIT_DISABLED; - policy.inferred = false; - policy.clearSnooze(); - } - } - setNetworkPolicies(policies); - - // Turn restrict background data off - setRestrictBackground(false); - - // Remove app's "restrict background data" flag - for (int uid : getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) { - setUidPolicy(uid, NetworkPolicyManager.POLICY_NONE); + try { + mService.factoryReset(subscriber); + } catch (RemoteException e) { } } diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl index 9599308..c027d54 100644 --- a/core/java/android/nfc/IAppCallback.aidl +++ b/core/java/android/nfc/IAppCallback.aidl @@ -24,7 +24,7 @@ import android.nfc.Tag; */ interface IAppCallback { - BeamShareData createBeamShareData(); - void onNdefPushComplete(); + BeamShareData createBeamShareData(byte peerLlcpVersion); + void onNdefPushComplete(byte peerLlcpVersion); void onTagDiscovered(in Tag tag); } diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index d009295..76bd0ec 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -46,7 +46,6 @@ public final class NfcActivityManager extends IAppCallback.Stub static final Boolean DBG = false; final NfcAdapter mAdapter; - final NfcEvent mDefaultEvent; // cached NfcEvent (its currently always the same) // All objects in the lists are protected by this final List<NfcApplicationState> mApps; // Application(s) that have NFC state. Usually one @@ -200,7 +199,6 @@ public final class NfcActivityManager extends IAppCallback.Stub mAdapter = adapter; mActivities = new LinkedList<NfcActivityState>(); mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app - mDefaultEvent = new NfcEvent(mAdapter); } public void enableReaderMode(Activity activity, ReaderCallback callback, int flags, @@ -354,13 +352,14 @@ public final class NfcActivityManager extends IAppCallback.Stub /** Callback from NFC service, usually on binder thread */ @Override - public BeamShareData createBeamShareData() { + public BeamShareData createBeamShareData(byte peerLlcpVersion) { NfcAdapter.CreateNdefMessageCallback ndefCallback; NfcAdapter.CreateBeamUrisCallback urisCallback; NdefMessage message; Activity activity; Uri[] uris; int flags; + NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion); synchronized (NfcActivityManager.this) { NfcActivityState state = findResumedActivityState(); if (state == null) return null; @@ -375,10 +374,10 @@ public final class NfcActivityManager extends IAppCallback.Stub // Make callbacks without lock if (ndefCallback != null) { - message = ndefCallback.createNdefMessage(mDefaultEvent); + message = ndefCallback.createNdefMessage(event); } if (urisCallback != null) { - uris = urisCallback.createBeamUris(mDefaultEvent); + uris = urisCallback.createBeamUris(event); if (uris != null) { ArrayList<Uri> validUris = new ArrayList<Uri>(); for (Uri uri : uris) { @@ -412,7 +411,7 @@ public final class NfcActivityManager extends IAppCallback.Stub /** Callback from NFC service, usually on binder thread */ @Override - public void onNdefPushComplete() { + public void onNdefPushComplete(byte peerLlcpVersion) { NfcAdapter.OnNdefPushCompleteCallback callback; synchronized (NfcActivityManager.this) { NfcActivityState state = findResumedActivityState(); @@ -420,10 +419,10 @@ public final class NfcActivityManager extends IAppCallback.Stub callback = state.onNdefPushCompleteCallback; } - + NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion); // Make callback without lock if (callback != null) { - callback.onNdefPushComplete(mDefaultEvent); + callback.onNdefPushComplete(event); } } diff --git a/core/java/android/nfc/NfcEvent.java b/core/java/android/nfc/NfcEvent.java index 860700a..cf1d71a 100644 --- a/core/java/android/nfc/NfcEvent.java +++ b/core/java/android/nfc/NfcEvent.java @@ -38,7 +38,14 @@ public final class NfcEvent { */ public final NfcAdapter nfcAdapter; - NfcEvent(NfcAdapter nfcAdapter) { + /** + * The LLCP version of the peer associated with the NFC event. + * The major version is in the top nibble, the minor version is in the bottom nibble. + */ + public final byte peerLlcpVersion; + + NfcEvent(NfcAdapter nfcAdapter, byte peerLlcpVersion) { this.nfcAdapter = nfcAdapter; + this.peerLlcpVersion = peerLlcpVersion; } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 7c5ddee..4dfe0de 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -152,10 +152,15 @@ public abstract class BatteryStats implements Parcelable { private static final String[] STAT_NAMES = { "l", "c", "u" }; /** - * Bump the version on this if the checkin format changes. + * Current version of checkin data format. + */ + static final String CHECKIN_VERSION = "14"; + + /** + * Old version, we hit 9 and ran out of room, need to remove. */ private static final int BATTERY_STATS_CHECKIN_VERSION = 9; - + private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 private static final long BYTES_PER_GB = 1073741824; //1024^3 @@ -178,7 +183,9 @@ public abstract class BatteryStats implements Parcelable { private static final String BATTERY_DATA = "bt"; private static final String BATTERY_DISCHARGE_DATA = "dc"; private static final String BATTERY_LEVEL_DATA = "lv"; + private static final String GLOBAL_WIFI_DATA = "gwfl"; private static final String WIFI_DATA = "wfl"; + private static final String GLOBAL_BLUETOOTH_DATA = "gble"; private static final String MISC_DATA = "m"; private static final String GLOBAL_NETWORK_DATA = "gn"; private static final String HISTORY_STRING_POOL = "hsp"; @@ -195,8 +202,6 @@ public abstract class BatteryStats implements Parcelable { private static final String WIFI_SUPPL_STATE_COUNT_DATA = "wssc"; private static final String WIFI_SIGNAL_STRENGTH_TIME_DATA = "wsgt"; private static final String WIFI_SIGNAL_STRENGTH_COUNT_DATA = "wsgc"; - private static final String BLUETOOTH_STATE_TIME_DATA = "bst"; - private static final String BLUETOOTH_STATE_COUNT_DATA = "bsc"; private static final String POWER_USE_SUMMARY_DATA = "pws"; private static final String POWER_USE_ITEM_DATA = "pwi"; private static final String DISCHARGE_STEP_DATA = "dsd"; @@ -1055,22 +1060,23 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE_GPS_ON_FLAG = 1<<29; public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28; public static final int STATE_WIFI_SCAN_FLAG = 1<<27; - public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<26; + public static final int STATE_WIFI_RADIO_ACTIVE_FLAG = 1<<26; public static final int STATE_MOBILE_RADIO_ACTIVE_FLAG = 1<<25; // These are on the lower bits used for the command; if they change // we need to write another int of data. public static final int STATE_SENSOR_ON_FLAG = 1<<23; public static final int STATE_AUDIO_ON_FLAG = 1<<22; public static final int STATE_PHONE_SCANNING_FLAG = 1<<21; - public static final int STATE_SCREEN_ON_FLAG = 1<<20; - public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; - public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18; - public static final int STATE_CHARGING_FLAG = 1<<17; - public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16; + public static final int STATE_SCREEN_ON_FLAG = 1<<20; // consider moving to states2 + public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2 + // empty slot + // empty slot + public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16; public static final int MOST_INTERESTING_STATES = - STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG - | STATE_PHONE_IN_CALL_FLAG | STATE_BLUETOOTH_ON_FLAG; + STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG; + + public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES; public int states; @@ -1088,9 +1094,15 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_WIFI_ON_FLAG = 1<<28; public static final int STATE2_FLASHLIGHT_FLAG = 1<<27; public static final int STATE2_DEVICE_IDLE_FLAG = 1<<26; + public static final int STATE2_CHARGING_FLAG = 1<<25; + public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<24; + public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<23; public static final int MOST_INTERESTING_STATES2 = - STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG; + STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG + | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG; + + public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2; public int states2; @@ -1137,8 +1149,10 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_PACKAGE_UNINSTALLED = 0x000d; // Event for a package being uninstalled. public static final int EVENT_ALARM = 0x000e; + // Record that we have decided we need to collect new stats data. + public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000f; // Number of event types. - public static final int EVENT_COUNT = 0x000f; + public static final int EVENT_COUNT = 0x0010; // Mask to extract out only the type part of the event. public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); @@ -1750,14 +1764,12 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock", "Wl"), new BitDescription(HistoryItem.STATE_WIFI_SCAN_FLAG, "wifi_scan", "Ws"), new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast", "Wm"), + new BitDescription(HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG, "wifi_radio", "Wr"), new BitDescription(HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG, "mobile_radio", "Pr"), new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"), new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"), new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"), new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"), - new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"), - new BitDescription(HistoryItem.STATE_CHARGING_FLAG, "charging", "ch"), - new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"), new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK, HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn", DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES), @@ -1778,10 +1790,13 @@ public abstract class BatteryStats implements Parcelable { = new BitDescription[] { new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"), new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"), - new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Wr"), + new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"), new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"), new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"), new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_FLAG, "device_idle", "di"), + new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"), + new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"), + new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"), new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK, HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss", new String[] { "0", "1", "2", "3", "4" }, @@ -1793,12 +1808,12 @@ public abstract class BatteryStats implements Parcelable { public static final String[] HISTORY_EVENT_NAMES = new String[] { "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn", - "motion", "active", "pkginst", "pkgunin", "alarm" + "motion", "active", "pkginst", "pkgunin", "alarm", "stats" }; public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] { "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn", - "Esm", "Eac", "Epi", "Epu", "Eal" + "Esm", "Eac", "Epi", "Epu", "Eal", "Est" }; /** @@ -1883,43 +1898,6 @@ public abstract class BatteryStats implements Parcelable { public abstract int getWifiSignalStrengthCount(int strengthBin, int which); /** - * Returns the time in microseconds that bluetooth has been on while the device was - * running on battery. - * - * {@hide} - */ - public abstract long getBluetoothOnTime(long elapsedRealtimeUs, int which); - - public abstract int getBluetoothPingCount(); - - public static final int BLUETOOTH_STATE_INACTIVE = 0; - public static final int BLUETOOTH_STATE_LOW = 1; - public static final int BLUETOOTH_STATE_MEDIUM = 2; - public static final int BLUETOOTH_STATE_HIGH = 3; - - static final String[] BLUETOOTH_STATE_NAMES = { - "inactive", "low", "med", "high" - }; - - public static final int NUM_BLUETOOTH_STATES = BLUETOOTH_STATE_HIGH +1; - - /** - * Returns the time in microseconds that Bluetooth has been running in the - * given active state. - * - * {@hide} - */ - public abstract long getBluetoothStateTime(int bluetoothState, - long elapsedRealtimeUs, int which); - - /** - * Returns the number of times that Bluetooth has entered the given active state. - * - * {@hide} - */ - public abstract int getBluetoothStateCount(int bluetoothState, int which); - - /** * Returns the time in microseconds that the flashlight has been on while the device was * running on battery. * @@ -2431,9 +2409,6 @@ public abstract class BatteryStats implements Parcelable { final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which); final int connChanges = getNumConnectivityChange(which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); - final long wifiOnTime = getWifiOnTime(rawRealtime, which); - final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); - final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); final StringBuilder sb = new StringBuilder(128); @@ -2475,7 +2450,8 @@ public abstract class BatteryStats implements Parcelable { } } } - + + // Dump network stats final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which); final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which); final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which); @@ -2484,19 +2460,34 @@ public abstract class BatteryStats implements Parcelable { final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which); final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which); final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which); - - // Dump network stats dumpLine(pw, 0 /* uid */, category, GLOBAL_NETWORK_DATA, mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes, mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets); + // Dump Wifi controller stats + final long wifiOnTime = getWifiOnTime(rawRealtime, which); + final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); + final long wifiIdleTimeMs = getWifiControllerActivity(CONTROLLER_IDLE_TIME, which); + final long wifiRxTimeMs = getWifiControllerActivity(CONTROLLER_RX_TIME, which); + final long wifiTxTimeMs = getWifiControllerActivity(CONTROLLER_TX_TIME, which); + final long wifiPowerMaMs = getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which); + dumpLine(pw, 0 /* uid */, category, GLOBAL_WIFI_DATA, + wifiOnTime / 1000, wifiRunningTime / 1000, + wifiIdleTimeMs, wifiRxTimeMs, wifiTxTimeMs, wifiPowerMaMs / (1000*60*60)); + + // Dump Bluetooth controller stats + final long btIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which); + final long btRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which); + final long btTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which); + final long btPowerMaMs = getBluetoothControllerActivity(CONTROLLER_POWER_DRAIN, which); + dumpLine(pw, 0 /* uid */, category, GLOBAL_BLUETOOTH_DATA, + btIdleTimeMs, btRxTimeMs, btTxTimeMs, btPowerMaMs / (1000*60*60)); + // Dump misc stats dumpLine(pw, 0 /* uid */, category, MISC_DATA, - screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000, - wifiRunningTime / 1000, bluetoothOnTime / 1000, - mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes, + screenOnTime / 1000, phoneOnTime / 1000, fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000, - 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000, + getMobileRadioActiveTime(rawRealtime, which) / 1000, getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000, powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000, getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000, @@ -2566,17 +2557,6 @@ public abstract class BatteryStats implements Parcelable { } dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args); - // Dump bluetooth state stats - args = new Object[NUM_BLUETOOTH_STATES]; - for (int i=0; i<NUM_BLUETOOTH_STATES; i++) { - args[i] = getBluetoothStateTime(i, rawRealtime, which) / 1000; - } - dumpLine(pw, 0 /* uid */, category, BLUETOOTH_STATE_TIME_DATA, args); - for (int i=0; i<NUM_BLUETOOTH_STATES; i++) { - args[i] = getBluetoothStateCount(i, which); - } - dumpLine(pw, 0 /* uid */, category, BLUETOOTH_STATE_COUNT_DATA, args); - if (which == STATS_SINCE_UNPLUGGED) { dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(), getDischargeCurrentLevel()); @@ -2681,6 +2661,7 @@ public abstract class BatteryStats implements Parcelable { continue; } final Uid u = uidStats.valueAt(iu); + // Dump Network stats per uid, if any final long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which); final long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which); @@ -2692,11 +2673,6 @@ public abstract class BatteryStats implements Parcelable { final int mobileActiveCount = u.getMobileRadioActiveCount(which); final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which); final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which); - final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which); - final long wifiScanTime = u.getWifiScanTime(rawRealtime, which); - final int wifiScanCount = u.getWifiScanCount(which); - final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); - if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0 || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0 || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0) { @@ -2707,10 +2683,19 @@ public abstract class BatteryStats implements Parcelable { mobileActiveTime, mobileActiveCount); } + final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which); + final long wifiScanTime = u.getWifiScanTime(rawRealtime, which); + final int wifiScanCount = u.getWifiScanCount(which); + final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); + final long uidWifiIdleTimeMs = u.getWifiControllerActivity(CONTROLLER_IDLE_TIME, which); + final long uidWifiRxTimeMs = u.getWifiControllerActivity(CONTROLLER_RX_TIME, which); + final long uidWifiTxTimeMs = u.getWifiControllerActivity(CONTROLLER_TX_TIME, which); if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0 - || uidWifiRunningTime != 0) { + || uidWifiRunningTime != 0 || uidWifiIdleTimeMs != 0 || uidWifiRxTimeMs != 0 + || uidWifiTxTimeMs != 0) { dumpLine(pw, uid, category, WIFI_DATA, - fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount); + fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount, + uidWifiIdleTimeMs, uidWifiRxTimeMs, uidWifiTxTimeMs); } if (u.hasUserActivity()) { @@ -2968,7 +2953,6 @@ public abstract class BatteryStats implements Parcelable { final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); - final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); sb.setLength(0); sb.append(prefix); sb.append(" Screen on: "); formatTimeMs(sb, screenOnTime / 1000); @@ -3317,42 +3301,11 @@ public abstract class BatteryStats implements Parcelable { sb.setLength(0); sb.append(prefix); - sb.append(" WiFi Energy use: ").append(BatteryStatsHelper.makemAh( + sb.append(" WiFi Power drain: ").append(BatteryStatsHelper.makemAh( getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which) / (double)(1000*60*60))); sb.append(" mAh"); pw.println(sb.toString()); - sb.setLength(0); - sb.append(prefix); - sb.append(" Bluetooth on: "); formatTimeMs(sb, bluetoothOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(bluetoothOnTime, whichBatteryRealtime)); - sb.append(")"); - pw.println(sb.toString()); - - sb.setLength(0); - sb.append(prefix); - sb.append(" Bluetooth states:"); - didOne = false; - for (int i=0; i<NUM_BLUETOOTH_STATES; i++) { - final long time = getBluetoothStateTime(i, rawRealtime, which); - if (time == 0) { - continue; - } - sb.append("\n "); - didOne = true; - sb.append(BLUETOOTH_STATE_NAMES[i]); - sb.append(" "); - formatTimeMs(sb, time/1000); - sb.append("("); - sb.append(formatRatioLocked(time, whichBatteryRealtime)); - sb.append(") "); - sb.append(getPhoneDataConnectionCount(i, which)); - sb.append("x"); - } - - if (!didOne) sb.append(" (no activity)"); - pw.println(sb.toString()); - final long bluetoothIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which); final long bluetoothRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which); @@ -3384,6 +3337,14 @@ public abstract class BatteryStats implements Parcelable { sb.append(")"); pw.println(sb.toString()); + sb.setLength(0); + sb.append(prefix); + sb.append(" Bluetooth Power drain: ").append(BatteryStatsHelper.makemAh( + getBluetoothControllerActivity(CONTROLLER_POWER_DRAIN, which) / + (double)(1000*60*60))); + sb.append(" mAh"); + pw.println(sb.toString()); + pw.println(); if (which == STATS_SINCE_UNPLUGGED) { @@ -4883,7 +4844,8 @@ public abstract class BatteryStats implements Parcelable { prepareForDumpLocked(); dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA, - "13", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion()); + CHECKIN_VERSION, getParcelVersion(), getStartPlatformVersion(), + getEndPlatformVersion()); long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 9d8a1ba..af23f11 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -283,6 +283,8 @@ public final class Parcel { private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName); private static native void nativeEnforceInterface(long nativePtr, String interfaceName); + private static native long nativeGetBlobAshmemSize(long nativePtr); + public final static Parcelable.Creator<String> STRING_CREATOR = new Parcelable.Creator<String>() { public String createFromParcel(Parcel source) { @@ -2594,4 +2596,11 @@ public final class Parcel { N--; } } + + /** + * @hide For testing + */ + public long getBlobAshmemSize() { + return nativeGetBlobAshmemSize(mNativePtr); + } } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b9e307f..44eb1ed 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -466,9 +466,9 @@ public class UserManager { } /** - * Returns the user handle for the user that the calling process is running on. + * Returns the user handle for the user that this process is running under. * - * @return the user handle of the user making this call. + * @return the user handle of this process. * @hide */ public int getUserHandle() { @@ -505,6 +505,17 @@ public class UserManager { } /** + * Used to check if this process is running under the system user. The system user + * is the initial user that is implicitly created on first boot and hosts most of the + * system services. + * + * @return whether this process is running under the system user. + */ + public boolean isSystemUser() { + return UserHandle.myUserId() == UserHandle.USER_OWNER; + } + + /** * Used to check if the user making this call is linked to another user. Linked users may have * a reduced number of available apps, app restrictions and account restrictions. * @return whether the user making this call is a linked user diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 2c60d41..64f2a05 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.DebugUtils; import com.android.internal.util.IndentingPrintWriter; @@ -46,7 +47,6 @@ public class DiskInfo implements Parcelable { public final int flags; public long size; public String label; - public String[] volumeIds; public DiskInfo(String id, int flags) { this.id = Preconditions.checkNotNull(id); @@ -58,19 +58,39 @@ public class DiskInfo implements Parcelable { flags = parcel.readInt(); size = parcel.readLong(); label = parcel.readString(); - volumeIds = parcel.readStringArray(); } public @NonNull String getId() { return id; } + private boolean isInteresting(String label) { + if (TextUtils.isEmpty(label)) { + return false; + } + if (label.equalsIgnoreCase("ata")) { + return false; + } + if (label.toLowerCase().contains("generic")) { + return false; + } + return true; + } + public String getDescription() { - // TODO: splice vendor label into these strings + final Resources res = Resources.getSystem(); if ((flags & FLAG_SD) != 0) { - return Resources.getSystem().getString(com.android.internal.R.string.storage_sd_card); + if (isInteresting(label)) { + return res.getString(com.android.internal.R.string.storage_sd_card_label, label); + } else { + return res.getString(com.android.internal.R.string.storage_sd_card); + } } else if ((flags & FLAG_USB) != 0) { - return Resources.getSystem().getString(com.android.internal.R.string.storage_usb); + if (isInteresting(label)) { + return res.getString(com.android.internal.R.string.storage_usb_drive_label, label); + } else { + return res.getString(com.android.internal.R.string.storage_usb_drive); + } } else { return null; } @@ -96,13 +116,11 @@ public class DiskInfo implements Parcelable { } public void dump(IndentingPrintWriter pw) { - pw.println("DiskInfo:"); + pw.println("DiskInfo{" + id + "}:"); pw.increaseIndent(); - pw.printPair("id", id); pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); pw.printPair("size", size); pw.printPair("label", label); - pw.printPair("volumeIds", volumeIds); pw.decreaseIndent(); pw.println(); } @@ -156,6 +174,5 @@ public class DiskInfo implements Parcelable { parcel.writeInt(this.flags); parcel.writeLong(size); parcel.writeString(label); - parcel.writeStringArray(volumeIds); } } diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java index fd914bc..8e878a4 100644 --- a/core/java/android/os/storage/IMountServiceListener.java +++ b/core/java/android/os/storage/IMountServiceListener.java @@ -98,6 +98,13 @@ public interface IMountServiceListener extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_onDiskUnsupported: { + data.enforceInterface(DESCRIPTOR); + final DiskInfo disk = (DiskInfo) data.readParcelable(null); + onDiskUnsupported(disk); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -198,6 +205,22 @@ public interface IMountServiceListener extends IInterface { _data.recycle(); } } + + @Override + public void onDiskUnsupported(DiskInfo disk) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeParcelable(disk, 0); + mRemote.transact(Stub.TRANSACTION_onDiskUnsupported, _data, _reply, + android.os.IBinder.FLAG_ONEWAY); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } } static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0); @@ -206,6 +229,7 @@ public interface IMountServiceListener extends IInterface { static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3); + static final int TRANSACTION_onDiskUnsupported = (IBinder.FIRST_CALL_TRANSACTION + 4); } /** @@ -230,4 +254,6 @@ public interface IMountServiceListener extends IInterface { throws RemoteException; public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException; + + public void onDiskUnsupported(DiskInfo disk) throws RemoteException; } diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java index 28a187d..ad2fae0 100644 --- a/core/java/android/os/storage/StorageEventListener.java +++ b/core/java/android/os/storage/StorageEventListener.java @@ -43,4 +43,7 @@ public class StorageEventListener { public void onVolumeMetadataChanged(VolumeInfo vol) { } + + public void onDiskUnsupported(DiskInfo disk) { + } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 0e977ff..f101352 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -69,6 +69,8 @@ public class StorageManager { /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; + /** {@hide} */ + public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ public static final int FLAG_ALL_METADATA = 1 << 0; @@ -87,6 +89,7 @@ public class StorageManager { private static final int MSG_STORAGE_STATE_CHANGED = 1; private static final int MSG_VOLUME_STATE_CHANGED = 2; private static final int MSG_VOLUME_METADATA_CHANGED = 3; + private static final int MSG_DISK_UNSUPPORTED = 4; final StorageEventListener mCallback; final Handler mHandler; @@ -113,6 +116,10 @@ public class StorageManager { mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1); args.recycle(); return true; + case MSG_DISK_UNSUPPORTED: + mCallback.onDiskUnsupported((DiskInfo) args.arg1); + args.recycle(); + return true; } args.recycle(); return false; @@ -147,6 +154,13 @@ public class StorageManager { args.arg1 = vol; mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); } + + @Override + public void onDiskUnsupported(DiskInfo disk) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = disk; + mHandler.obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget(); + } } /** @@ -494,6 +508,16 @@ public class StorageManager { } /** {@hide} */ + public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { + return findVolumeById(emulatedVol.getId().replace("emulated", "private")); + } + + /** {@hide} */ + public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { + return findVolumeById(privateVol.getId().replace("private", "emulated")); + } + + /** {@hide} */ public @NonNull List<VolumeInfo> getVolumes() { return getVolumes(0); } @@ -511,10 +535,9 @@ public class StorageManager { public @Nullable String getBestVolumeDescription(VolumeInfo vol) { String descrip = vol.getDescription(); - if (vol.diskId != null) { - final DiskInfo disk = findDiskById(vol.diskId); - if (disk != null && TextUtils.isEmpty(descrip)) { - descrip = disk.getDescription(); + if (vol.disk != null) { + if (TextUtils.isEmpty(descrip)) { + descrip = vol.disk.getDescription(); } } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index a241728..f3498d5 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -49,7 +49,10 @@ import java.util.Objects; * @hide */ public class VolumeInfo implements Parcelable { - public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID"; + public static final String ACTION_VOLUME_STATE_CHANGED = + "android.os.storage.action.VOLUME_STATE_CHANGED"; + public static final String EXTRA_VOLUME_ID = + "android.os.storage.extra.VOLUME_ID"; /** Stub volume representing internal private storage */ public static final String ID_PRIVATE_INTERNAL = "private"; @@ -63,15 +66,17 @@ public class VolumeInfo implements Parcelable { public static final int TYPE_OBB = 4; public static final int STATE_UNMOUNTED = 0; - public static final int STATE_MOUNTING = 1; + public static final int STATE_CHECKING = 1; public static final int STATE_MOUNTED = 2; - public static final int STATE_FORMATTING = 3; - public static final int STATE_UNMOUNTING = 4; - public static final int STATE_UNMOUNTABLE = 5; - public static final int STATE_REMOVED = 6; + public static final int STATE_MOUNTED_READ_ONLY = 3; + public static final int STATE_FORMATTING = 4; + public static final int STATE_EJECTING = 5; + public static final int STATE_UNMOUNTABLE = 6; + public static final int STATE_REMOVED = 7; + public static final int STATE_BAD_REMOVAL = 8; - public static final int FLAG_PRIMARY = 1 << 0; - public static final int FLAG_VISIBLE = 1 << 1; + public static final int MOUNT_FLAG_PRIMARY = 1 << 0; + public static final int MOUNT_FLAG_VISIBLE = 1 << 1; public static final int USER_FLAG_INITED = 1 << 0; public static final int USER_FLAG_SNOOZED = 1 << 1; @@ -97,26 +102,31 @@ public class VolumeInfo implements Parcelable { static { sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED); - sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING); + sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING); sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED); + sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY); sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED); - sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTING, Environment.MEDIA_EJECTING); + sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING); sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE); sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED); + sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL); sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED); sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING); sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED); + sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED); sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT); sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE); sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED); + sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL); } /** vold state */ public final String id; public final int type; - public int flags = 0; - public int userId = -1; + public final DiskInfo disk; + public int mountFlags = 0; + public int mountUserId = -1; public int state = STATE_UNMOUNTED; public String fsType; public String fsUuid; @@ -125,28 +135,32 @@ public class VolumeInfo implements Parcelable { /** Framework state */ public final int mtpIndex; - public String diskId; public String nickname; public int userFlags = 0; - public VolumeInfo(String id, int type, int mtpIndex) { + public VolumeInfo(String id, int type, DiskInfo disk, int mtpIndex) { this.id = Preconditions.checkNotNull(id); this.type = type; + this.disk = disk; this.mtpIndex = mtpIndex; } public VolumeInfo(Parcel parcel) { id = parcel.readString(); type = parcel.readInt(); - flags = parcel.readInt(); - userId = parcel.readInt(); + if (parcel.readInt() != 0) { + disk = DiskInfo.CREATOR.createFromParcel(parcel); + } else { + disk = null; + } + mountFlags = parcel.readInt(); + mountUserId = parcel.readInt(); state = parcel.readInt(); fsType = parcel.readString(); fsUuid = parcel.readString(); fsLabel = parcel.readString(); path = parcel.readString(); mtpIndex = parcel.readInt(); - diskId = parcel.readString(); nickname = parcel.readString(); userFlags = parcel.readInt(); } @@ -176,8 +190,12 @@ public class VolumeInfo implements Parcelable { return id; } + public @Nullable DiskInfo getDisk() { + return disk; + } + public @Nullable String getDiskId() { - return diskId; + return (disk != null) ? disk.id : null; } public int getType() { @@ -196,6 +214,10 @@ public class VolumeInfo implements Parcelable { return nickname; } + public int getMountUserId() { + return mountUserId; + } + public @Nullable String getDescription() { if (ID_PRIVATE_INTERNAL.equals(id)) { return Resources.getSystem().getString(com.android.internal.R.string.storage_internal); @@ -208,12 +230,20 @@ public class VolumeInfo implements Parcelable { } } + public boolean isMountedReadable() { + return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY; + } + + public boolean isMountedWritable() { + return state == STATE_MOUNTED; + } + public boolean isPrimary() { - return (flags & FLAG_PRIMARY) != 0; + return (mountFlags & MOUNT_FLAG_PRIMARY) != 0; } public boolean isVisible() { - return (flags & FLAG_VISIBLE) != 0; + return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; } public boolean isInited() { @@ -225,7 +255,7 @@ public class VolumeInfo implements Parcelable { } public boolean isVisibleToUser(int userId) { - if (type == TYPE_PUBLIC && userId == this.userId) { + if (type == TYPE_PUBLIC && userId == this.mountUserId) { return isVisible(); } else if (type == TYPE_EMULATED) { return isVisible(); @@ -241,7 +271,7 @@ public class VolumeInfo implements Parcelable { public File getPathForUser(int userId) { if (path == null) { return null; - } else if (type == TYPE_PUBLIC && userId == this.userId) { + } else if (type == TYPE_PUBLIC && userId == this.mountUserId) { return new File(path); } else if (type == TYPE_EMULATED) { return new File(path, Integer.toString(userId)); @@ -250,6 +280,19 @@ public class VolumeInfo implements Parcelable { } } + /** + * Path which is accessible to apps holding + * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}. + */ + public File getInternalPathForUser(int userId) { + if (type == TYPE_PUBLIC) { + // TODO: plumb through cleaner path from vold + return new File(path.replace("/storage/", "/mnt/media_rw/")); + } else { + return getPathForUser(userId); + } + } + public StorageVolume buildStorageVolume(Context context, int userId) { final boolean removable; final boolean emulated; @@ -333,12 +376,12 @@ public class VolumeInfo implements Parcelable { } public void dump(IndentingPrintWriter pw) { - pw.println("VolumeInfo:"); + pw.println("VolumeInfo{" + id + "}:"); pw.increaseIndent(); - pw.printPair("id", id); pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type)); - pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); - pw.printPair("userId", userId); + pw.printPair("diskId", getDiskId()); + pw.printPair("mountFlags", DebugUtils.flagsToString(getClass(), "MOUNT_FLAG_", mountFlags)); + pw.printPair("mountUserId", mountUserId); pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state)); pw.println(); pw.printPair("fsType", fsType); @@ -347,7 +390,6 @@ public class VolumeInfo implements Parcelable { pw.println(); pw.printPair("path", path); pw.printPair("mtpIndex", mtpIndex); - pw.printPair("diskId", diskId); pw.printPair("nickname", nickname); pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags)); pw.decreaseIndent(); @@ -401,15 +443,20 @@ public class VolumeInfo implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(id); parcel.writeInt(type); - parcel.writeInt(this.flags); - parcel.writeInt(userId); + if (disk != null) { + parcel.writeInt(1); + disk.writeToParcel(parcel, flags); + } else { + parcel.writeInt(0); + } + parcel.writeInt(mountFlags); + parcel.writeInt(mountUserId); parcel.writeInt(state); parcel.writeString(fsType); parcel.writeString(fsUuid); parcel.writeString(fsLabel); parcel.writeString(path); parcel.writeInt(mtpIndex); - parcel.writeString(diskId); parcel.writeString(nickname); parcel.writeInt(userFlags); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 793971f..a622a21 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6033,6 +6033,13 @@ public final class Settings { public static final String DISPLAY_SIZE_FORCED = "display_size_forced"; /** + * The saved value for WindowManagerService.setForcedDisplayScalingMode(). + * 0 or unset if scaling is automatic, 1 if scaling is disabled. + * @hide + */ + public static final String DISPLAY_SCALING_FORCE = "display_scaling_force"; + + /** * The maximum size, in bytes, of a download that the download manager will transfer over * a non-wifi connection. * @hide diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java index 0b3bf45..7e87717 100644 --- a/core/java/android/security/NetworkSecurityPolicy.java +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -46,8 +46,8 @@ public class NetworkSecurityPolicy { * without TLS or STARTTLS) is permitted for this process. * * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and - * FTP stacks, {@link android.webkit.WebView}, {@link android.media.MediaPlayer}) will refuse - * this process's requests to use cleartext traffic. Third-party libraries are strongly + * FTP stacks, {@link android.app.DownloadManager}, {@link android.media.MediaPlayer}) will + * refuse this process's requests to use cleartext traffic. Third-party libraries are strongly * encouraged to honor this setting as well. * * <p>This flag is honored on a best effort basis because it's impossible to prevent all @@ -56,6 +56,8 @@ public class NetworkSecurityPolicy { * because it cannot determine whether its traffic is in cleartext. However, most network * traffic from applications is handled by higher-level network stacks/components which can * honor this aspect of the policy. + * + * <p>NOTE: {@link android.webkit.WebView} does not honor this flag. */ public boolean isCleartextTrafficPermitted() { return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted(); diff --git a/core/java/android/security/keymaster/KeymasterArgument.java b/core/java/android/security/keymaster/KeymasterArgument.java index 9a1c894..9adde35 100644 --- a/core/java/android/security/keymaster/KeymasterArgument.java +++ b/core/java/android/security/keymaster/KeymasterArgument.java @@ -42,6 +42,7 @@ abstract class KeymasterArgument implements Parcelable { case KeymasterDefs.KM_INT_REP: return new KeymasterIntArgument(tag, in); case KeymasterDefs.KM_LONG: + case KeymasterDefs.KM_LONG_REP: return new KeymasterLongArgument(tag, in); case KeymasterDefs.KM_DATE: return new KeymasterDateArgument(tag, in); diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java index 8ed288c..82f65c7 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.java +++ b/core/java/android/security/keymaster/KeymasterArguments.java @@ -63,6 +63,12 @@ public class KeymasterArguments implements Parcelable { } } + public void addLongs(int tag, long... values) { + for (long value : values) { + addLong(tag, value); + } + } + public void addBoolean(int tag) { mArguments.add(new KeymasterBooleanArgument(tag)); } @@ -111,8 +117,13 @@ public class KeymasterArguments implements Parcelable { } public long getLong(int tag, long defaultValue) { - if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG) { - throw new IllegalArgumentException("Tag is not a long type: " + tag); + switch (KeymasterDefs.getTagType(tag)) { + case KeymasterDefs.KM_LONG: + break; // Accepted type + case KeymasterDefs.KM_LONG_REP: + throw new IllegalArgumentException("Repeatable tags must use getLongs: " + tag); + default: + throw new IllegalArgumentException("Tag is not a long type: " + tag); } KeymasterArgument arg = getArgumentByTag(tag); if (arg == null) { @@ -175,6 +186,19 @@ public class KeymasterArguments implements Parcelable { return values; } + public List<Long> getLongs(int tag) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG_REP) { + throw new IllegalArgumentException("Tag is not a repeating long: " + tag); + } + List<Long> values = new ArrayList<Long>(); + for (KeymasterArgument arg : mArguments) { + if (arg.tag == tag) { + values.add(((KeymasterLongArgument) arg).value); + } + } + return values; + } + public int size() { return mArguments.size(); } diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index ab8a8b6..40baf9c 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -118,9 +118,9 @@ public final class KeymasterDefs { public static final int KM_DIGEST_SHA_2_512 = 6; // Key origins. - public static final int KM_ORIGIN_HARDWARE = 0; - public static final int KM_ORIGIN_SOFTWARE = 1; + public static final int KM_ORIGIN_GENERATED = 0; public static final int KM_ORIGIN_IMPORTED = 2; + public static final int KM_ORIGIN_UNKNOWN = 3; // Key usability requirements. public static final int KM_BLOB_STANDALONE = 0; diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java index 9d2be09..eb17b7e 100644 --- a/core/java/android/security/keymaster/KeymasterLongArgument.java +++ b/core/java/android/security/keymaster/KeymasterLongArgument.java @@ -28,6 +28,7 @@ class KeymasterLongArgument extends KeymasterArgument { super(tag); switch (KeymasterDefs.getTagType(tag)) { case KeymasterDefs.KM_LONG: + case KeymasterDefs.KM_LONG_REP: break; // OK. default: throw new IllegalArgumentException("Bad long tag " + tag); diff --git a/core/java/android/service/gatekeeper/IGateKeeperService.aidl b/core/java/android/service/gatekeeper/IGateKeeperService.aidl index 2f3e296..4f46701 100644 --- a/core/java/android/service/gatekeeper/IGateKeeperService.aidl +++ b/core/java/android/service/gatekeeper/IGateKeeperService.aidl @@ -53,13 +53,27 @@ interface IGateKeeperService { * Verifies an enrolled handle against a provided, plaintext blob. * @param uid The Android user ID associated to this enrollment * @param challenge a challenge to authenticate agaisnt the device credential. If successful - * authentication occurs, this value will be written to the returned + * authentication occurs, this value will be written to the returned * authentication attestation. * @param enrolledPasswordHandle The handle against which the provided password will be * verified. * @param The plaintext blob to verify against enrolledPassword. * @return an opaque attestation of authentication on success, or null. */ - byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle, + byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle, in byte[] providedPassword); + + /** + * Retrieves the secure identifier for the user with the provided Android ID, + * or 0 if none is found. + * @param uid the Android user id + */ + long getSecureUserId(int uid); + + /** + * Clears secure user id associated with the provided Android ID. + * Must be called when password is set to NONE. + * @param uid the Android user id. + */ + void clearSecureUserId(int uid); } diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl index 76b2be0..ec66cc8 100644 --- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl +++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl @@ -24,7 +24,7 @@ import android.os.UserHandle; * @hide */ oneway interface ITrustAgentServiceCallback { - void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser); + void grantTrust(CharSequence message, long durationMs, int flags); void revokeTrust(); void setManagingTrust(boolean managingTrust); void onConfigureCompleted(boolean result, IBinder token); diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index a3178e2..9d7ffad 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -17,6 +17,7 @@ package android.service.trust; import android.Manifest; +import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; @@ -32,6 +33,8 @@ import android.os.RemoteException; import android.util.Log; import android.util.Slog; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -69,6 +72,7 @@ import java.util.List; */ @SystemApi public class TrustAgentService extends Service { + private final String TAG = TrustAgentService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; private static final boolean DEBUG = false; @@ -86,6 +90,34 @@ public class TrustAgentService extends Service { */ public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent"; + + /** + * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that trust is being granted + * as the direct result of user action - such as solving a security challenge. The hint is used + * by the system to optimize the experience. Behavior may vary by device and release, so + * one should only set this parameter if it meets the above criteria rather than relying on + * the behavior of any particular device or release. + */ + public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1 << 0; + + /** + * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that the agent would like + * to dismiss the keyguard. When using this flag, the {@code TrustAgentService} must ensure + * it is only set in response to a direct user action with the expectation of dismissing the + * keyguard. + */ + public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 1 << 1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = { + FLAG_GRANT_TRUST_INITIATED_BY_USER, + FLAG_GRANT_TRUST_DISMISS_KEYGUARD, + }) + public @interface GrantTrustFlags {} + + private static final int MSG_UNLOCK_ATTEMPT = 1; private static final int MSG_CONFIGURE = 2; private static final int MSG_TRUST_TIMEOUT = 3; @@ -228,11 +260,35 @@ public class TrustAgentService extends Service { * direct result of user action - such as solving a security challenge. The hint is used * by the system to optimize the experience. Behavior may vary by device and release, so * one should only set this parameter if it meets the above criteria rather than relying on - * the behavior of any particular device or release. + * the behavior of any particular device or release. Corresponds to + * {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}. * @throws IllegalStateException if the agent is not currently managing trust. + * + * @deprecated use {@link #grantTrust(CharSequence, long, int)} instead. */ + @Deprecated public final void grantTrust( final CharSequence message, final long durationMs, final boolean initiatedByUser) { + grantTrust(message, durationMs, initiatedByUser ? FLAG_GRANT_TRUST_INITIATED_BY_USER : 0); + } + + /** + * Call to grant trust on the device. + * + * @param message describes why the device is trusted, e.g. "Trusted by location". + * @param durationMs amount of time in milliseconds to keep the device in a trusted state. + * Trust for this agent will automatically be revoked when the timeout expires unless + * extended by a subsequent call to this function. The timeout is measured from the + * invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}. + * For security reasons, the value should be no larger than necessary. + * The value may be adjusted by the system as necessary to comply with a policy controlled + * by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()} + * for determining when trust expires. + * @param flags TBDocumented + * @throws IllegalStateException if the agent is not currently managing trust. + */ + public final void grantTrust( + final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) { synchronized (mLock) { if (!mManagingTrust) { throw new IllegalStateException("Cannot grant trust if agent is not managing trust." @@ -240,7 +296,7 @@ public class TrustAgentService extends Service { } if (mCallback != null) { try { - mCallback.grantTrust(message.toString(), durationMs, initiatedByUser); + mCallback.grantTrust(message.toString(), durationMs, flags); } catch (RemoteException e) { onError("calling enableTrust()"); } @@ -250,7 +306,7 @@ public class TrustAgentService extends Service { mPendingGrantTrustTask = new Runnable() { @Override public void run() { - grantTrust(message, durationMs, initiatedByUser); + grantTrust(message, durationMs, flags); } }; } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 1674950..016541f 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -17,6 +17,7 @@ package android.service.wallpaper; import android.content.res.TypedArray; +import android.graphics.Canvas; import android.os.SystemProperties; import android.view.WindowInsets; @@ -185,6 +186,7 @@ public abstract class WallpaperService extends Service { DisplayManager mDisplayManager; Display mDisplay; + private int mDisplayState; final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { { @@ -228,7 +230,19 @@ public abstract class WallpaperService extends Service { throw new UnsupportedOperationException( "Wallpapers do not support keep screen on"); } - + + @Override + public Canvas lockCanvas() { + if (mDisplayState == Display.STATE_DOZE + || mDisplayState == Display.STATE_DOZE_SUSPEND) { + try { + mSession.pokeDrawLock(mWindow); + } catch (RemoteException e) { + // System server died, can be ignored. + } + } + return super.lockCanvas(); + } }; final class WallpaperInputEventReceiver extends InputEventReceiver { @@ -831,9 +845,12 @@ public abstract class WallpaperService extends Service { mWindow.setSession(mSession); + mLayout.packageName = getPackageName(); + mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); + mDisplayState = mDisplay.getState(); if (DEBUG) Log.v(TAG, "onCreate(): " + this); onCreate(mSurfaceHolder); @@ -873,8 +890,8 @@ public abstract class WallpaperService extends Service { void reportVisibility() { if (!mDestroyed) { - boolean visible = mVisible - & mDisplay != null && mDisplay.getState() != Display.STATE_OFF; + mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); + boolean visible = mVisible && mDisplayState != Display.STATE_OFF; if (mReportedVisible != visible) { mReportedVisible = visible; if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index 866137c..354c15f 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -81,29 +81,47 @@ public class TextDirectionHeuristics { private static final int STATE_FALSE = 1; private static final int STATE_UNKNOWN = 2; - private static int isRtlText(int directionality) { - switch (directionality) { - case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - return STATE_FALSE; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - return STATE_TRUE; - default: - return STATE_UNKNOWN; - } - } - - private static int isRtlTextOrFormat(int directionality) { - switch (directionality) { + /* Returns STATE_TRUE for strong RTL characters, STATE_FALSE for strong LTR characters, and + * STATE_UNKNOWN for everything else. + */ + private static int isRtlCodePoint(int codePoint) { + switch (Character.getDirectionality(codePoint)) { case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: return STATE_FALSE; case Character.DIRECTIONALITY_RIGHT_TO_LEFT: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: return STATE_TRUE; + case Character.DIRECTIONALITY_UNDEFINED: + // Unassigned characters still have bidi direction, defined at: + // http://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedBidiClass.txt + + if ((0x0590 <= codePoint && codePoint <= 0x08FF) || + (0xFB1D <= codePoint && codePoint <= 0xFDCF) || + (0xFDF0 <= codePoint && codePoint <= 0xFDFF) || + (0xFE70 <= codePoint && codePoint <= 0xFEFF) || + (0x10800 <= codePoint && codePoint <= 0x10FFF) || + (0x1E800 <= codePoint && codePoint <= 0x1EFFF)) { + // Unassigned RTL character + return STATE_TRUE; + } else if ( + // Potentially-unassigned Default_Ignorable. Ranges are from unassigned + // characters that have Unicode property Other_Default_Ignorable_Code_Point + // plus some enlargening to cover bidi isolates and simplify checks. + (0x2065 <= codePoint && codePoint <= 0x2069) || + (0xFFF0 <= codePoint && codePoint <= 0xFFF8) || + (0xE0000 <= codePoint && codePoint <= 0xE0FFF) || + // Non-character + (0xFDD0 <= codePoint && codePoint <= 0xFDEF) || + ((codePoint & 0xFFFE) == 0xFFFE) || + // Currency symbol + (0x20A0 <= codePoint && codePoint <= 0x20CF) || + // Unpaired surrogate + (0xD800 <= codePoint && codePoint <= 0xDFFF)) { + return STATE_UNKNOWN; + } else { + // Unassigned LTR character + return STATE_FALSE; + } default: return STATE_UNKNOWN; } @@ -181,14 +199,26 @@ public class TextDirectionHeuristics { /** * Algorithm that uses the first strong directional character to determine the paragraph - * direction. This is the standard Unicode Bidirectional algorithm. + * direction. This is the standard Unicode Bidirectional Algorithm (steps P2 and P3), with the + * exception that if no strong character is found, UNKNOWN is returned. */ private static class FirstStrong implements TextDirectionAlgorithm { @Override public int checkRtl(CharSequence cs, int start, int count) { int result = STATE_UNKNOWN; - for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) { - result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i))); + int openIsolateCount = 0; + for (int cp, i = start, end = start + count; + i < end && result == STATE_UNKNOWN; + i += Character.charCount(cp)) { + cp = Character.codePointAt(cs, i); + if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates + openIsolateCount += 1; + } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI) + if (openIsolateCount > 0) openIsolateCount -= 1; + } else if (openIsolateCount == 0) { + // Only consider the characters outside isolate pairs + result = isRtlCodePoint(cp); + } } return result; } @@ -200,9 +230,10 @@ public class TextDirectionHeuristics { } /** - * Algorithm that uses the presence of any strong directional non-format - * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the - * direction of text. + * Algorithm that uses the presence of any strong directional character of the type indicated + * in the constructor parameter to determine the direction of text. + * + * Characters inside isolate pairs are skipped. */ private static class AnyStrong implements TextDirectionAlgorithm { private final boolean mLookForRtl; @@ -210,22 +241,31 @@ public class TextDirectionHeuristics { @Override public int checkRtl(CharSequence cs, int start, int count) { boolean haveUnlookedFor = false; - for (int i = start, e = start + count; i < e; ++i) { - switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) { - case STATE_TRUE: - if (mLookForRtl) { - return STATE_TRUE; - } - haveUnlookedFor = true; - break; - case STATE_FALSE: - if (!mLookForRtl) { - return STATE_FALSE; - } - haveUnlookedFor = true; - break; - default: - break; + int openIsolateCount = 0; + for (int cp, i = start, end = start + count; i < end; i += Character.charCount(cp)) { + cp = Character.codePointAt(cs, i); + if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates + openIsolateCount += 1; + } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI) + if (openIsolateCount > 0) openIsolateCount -= 1; + } else if (openIsolateCount == 0) { + // Only consider the characters outside isolate pairs + switch (isRtlCodePoint(cp)) { + case STATE_TRUE: + if (mLookForRtl) { + return STATE_TRUE; + } + haveUnlookedFor = true; + break; + case STATE_FALSE: + if (!mLookForRtl) { + return STATE_FALSE; + } + haveUnlookedFor = true; + break; + default: + break; + } } } if (haveUnlookedFor) { diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index fcc3a40..de509b2 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -240,23 +240,30 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme boolean handled = Touch.onTouchEvent(widget, buffer, event); - if (widget.isFocused() && !widget.didTouchFocusSelect()) { - if (action == MotionEvent.ACTION_DOWN) { - // Capture the mouse pointer down location to ensure selection starts - // right under the mouse (and is not influenced by cursor location). - // The code below needs to run for mouse events. - // For touch events, the code should run only when selection is active. - if (isMouse || isTouchSelecting(isMouse, buffer)) { - int offset = widget.getOffsetForPosition(event.getX(), event.getY()); - buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); - // Disallow intercepting of the touch events, so that - // users can scroll and select at the same time. - // without this, users would get booted out of select - // mode once the view detected it needed to scroll. - widget.getParent().requestDisallowInterceptTouchEvent(true); + if (widget.didTouchFocusSelect() && !isMouse) { + return handled; + } + if (action == MotionEvent.ACTION_DOWN) { + // Capture the mouse pointer down location to ensure selection starts + // right under the mouse (and is not influenced by cursor location). + // The code below needs to run for mouse events. + // For touch events, the code should run only when selection is active. + if (isMouse || isTouchSelecting(isMouse, buffer)) { + if (!widget.isFocused()) { + if (!widget.requestFocus()) { + return handled; + } } - } else if (action == MotionEvent.ACTION_MOVE) { - + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); + buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); + // Disallow intercepting of the touch events, so that + // users can scroll and select at the same time. + // without this, users would get booted out of select + // mode once the view detected it needed to scroll. + widget.getParent().requestDisallowInterceptTouchEvent(true); + } + } else if (widget.isFocused()) { + if (action == MotionEvent.ACTION_MOVE) { // Cursor can be active at any location in the text while mouse pointer can start // selection from a totally different location. Use LAST_TAP_DOWN span to ensure // text selection will start from mouse pointer location. diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index 07c1ec3..fe7571f 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -97,7 +97,7 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener // Delete a character. final int start = Selection.getSelectionEnd(content); final int end; - if (isForwardDelete || event.isShiftPressed() || isShiftActive) { + if (isForwardDelete) { end = TextUtils.getOffsetAfter(content, start); } else { end = TextUtils.getOffsetBefore(content, start); diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 71863b7..71e2251 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -170,6 +170,15 @@ public final class Display { public static final int FLAG_PRESENTATION = 1 << 3; /** + * Display flag: Indicates that the contents of the display should not be scaled + * to fit the physical screen dimensions. Used for development only to emulate + * devices with smaller physicals screens while preserving density. + * + * @hide + */ + public static final int FLAG_SCALING_DISABLED = 1 << 30; + + /** * Display type: Unknown display type. * @hide */ diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index de46a4a..5a9a1ea 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -22,6 +22,8 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import java.lang.ref.WeakReference; + /** * Provides a low-level mechanism for an application to receive display events * such as vertical sync. @@ -42,7 +44,7 @@ public abstract class DisplayEventReceiver { // GC'd while the native peer of the receiver is using them. private MessageQueue mMessageQueue; - private static native long nativeInit(DisplayEventReceiver receiver, + private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, MessageQueue messageQueue); private static native void nativeDispose(long receiverPtr); private static native void nativeScheduleVsync(long receiverPtr); @@ -58,7 +60,7 @@ public abstract class DisplayEventReceiver { } mMessageQueue = looper.getQueue(); - mReceiverPtr = nativeInit(this, mMessageQueue); + mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index ecf45b4..243961c 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -538,6 +538,9 @@ public final class DisplayInfo implements Parcelable { if ((flags & Display.FLAG_PRESENTATION) != 0) { result.append(", FLAG_PRESENTATION"); } + if ((flags & Display.FLAG_SCALING_DISABLED) != 0) { + result.append(", FLAG_SCALING_DISABLED"); + } return result.toString(); } } diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index e9f2353..eedbc70 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -88,10 +88,10 @@ public class DisplayListCanvas extends Canvas { /////////////////////////////////////////////////////////////////////////// private DisplayListCanvas() { - super(nCreateDisplayListRenderer()); + super(nCreateDisplayListCanvas()); } - private static native long nCreateDisplayListRenderer(); + private static native long nCreateDisplayListCanvas(); public static void setProperty(String name, String value) { nSetProperty(name, value); @@ -283,7 +283,7 @@ public class DisplayListCanvas extends Canvas { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk, + nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } @@ -293,11 +293,11 @@ public class DisplayListCanvas extends Canvas { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk, + nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } - private static native void nDrawPatch(long renderer, Bitmap bitmap, long chunk, + private static native void nDrawPatch(long renderer, long bitmap, long chunk, float left, float top, float right, float bottom, long paint); public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index d6625c8..5994d4f 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -70,6 +70,7 @@ interface IWindowManager int getBaseDisplayDensity(int displayId); void setForcedDisplayDensity(int displayId, int density); void clearForcedDisplayDensity(int displayId); + void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable void setOverscan(int displayId, int left, int top, int right, int bottom); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 779560c..1ac3f45 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -747,8 +747,22 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int KEYCODE_TV_TIMER_PROGRAMMING = 258; /** Key code constant: Help key. */ public static final int KEYCODE_HELP = 259; - - private static final int LAST_KEYCODE = KEYCODE_HELP; + /** Key code constant: Navigate to previous key. + * Goes backward by one item in an ordered collection of items. */ + public static final int KEYCODE_NAVIGATE_PREVIOUS = 260; + /** Key code constant: Navigate to next key. + * Advances to the next item in an ordered collection of items. */ + public static final int KEYCODE_NAVIGATE_NEXT = 261; + /** Key code constant: Navigate in key. + * Activates the item that currently has focus or expands to the next level of a navigation + * hierarchy. */ + public static final int KEYCODE_NAVIGATE_IN = 262; + /** Key code constant: Navigate out key. + * Backs out one level of a navigation hierarchy or collapses the item that currently has + * focus. */ + public static final int KEYCODE_NAVIGATE_OUT = 263; + + private static final int LAST_KEYCODE = KEYCODE_NAVIGATE_OUT; // NOTE: If you add a new keycode here you must also add it to: // isSystem() diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 25c5127..87d5d9a 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -360,7 +360,7 @@ public class ThreadedRenderer extends HardwareRenderer { @Override boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { return nCopyLayerInto(mNativeProxy, - layer.getDeferredLayerUpdater(), bitmap); + layer.getDeferredLayerUpdater(), bitmap.getSkBitmap()); } @Override @@ -460,6 +460,8 @@ public class ThreadedRenderer extends HardwareRenderer { if (buffer != null) { long[] map = atlas.getMap(); if (map != null) { + // TODO Remove after fixing b/15425820 + validateMap(context, map); nSetAtlas(renderProxy, buffer, map); } // If IAssetAtlas is not the same class as the IBinder @@ -474,6 +476,32 @@ public class ThreadedRenderer extends HardwareRenderer { Log.w(LOG_TAG, "Could not acquire atlas", e); } } + + private static void validateMap(Context context, long[] map) { + Log.d("Atlas", "Validating map..."); + HashSet<Long> preloadedPointers = new HashSet<Long>(); + + // We only care about drawables that hold bitmaps + final Resources resources = context.getResources(); + final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables(); + + final int count = drawables.size(); + ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>(); + for (int i = 0; i < count; i++) { + drawables.valueAt(i).addAtlasableBitmaps(tmpList); + for (int j = 0; j < tmpList.size(); j++) { + preloadedPointers.add(tmpList.get(j).getSkBitmap()); + } + tmpList.clear(); + } + + for (int i = 0; i < map.length; i += 4) { + if (!preloadedPointers.contains(map[i])) { + Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i])); + map[i] = 0; + } + } + } } static native void setupShadersDiskCache(String cacheFile); @@ -503,7 +531,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native long nCreateTextureLayer(long nativeProxy); private static native void nBuildLayer(long nativeProxy, long node); - private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap); + private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap); private static native void nPushLayerUpdate(long nativeProxy, long layer); private static native void nCancelLayerUpdate(long nativeProxy, long layer); private static native void nDetachSurfaceTexture(long nativeProxy, long layer); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 25fa349..9741239 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15101,6 +15101,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mClipBounds != null) ? new Rect(mClipBounds) : null; } + + /** + * Populates an output rectangle with the clip bounds of the view, + * returning {@code true} if successful or {@code false} if the view's + * clip bounds are {@code null}. + * + * @param outRect rectangle in which to place the clip bounds of the view + * @return {@code true} if successful or {@code false} if the view's + * clip bounds are {@code null} + */ + public boolean getClipBounds(Rect outRect) { + if (mClipBounds != null) { + outRect.set(mClipBounds); + return true; + } + return false; + } + /** * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view. @@ -15283,9 +15301,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, RenderNode renderNode = null; Bitmap cache = null; - int layerType = getLayerType(); + int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local if (layerType == LAYER_TYPE_SOFTWARE || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) { + // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); cache = getDrawingCache(true); @@ -15312,10 +15331,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sy = mScrollY; } - final boolean hasNoCache = cache == null || drawingWithRenderNode; - final boolean offsetForScroll = cache == null - && !drawingWithRenderNode - && layerType != LAYER_TYPE_HARDWARE; + final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; + final boolean offsetForScroll = cache == null && !drawingWithRenderNode; int restoreTo = -1; if (!drawingWithRenderNode || transformToApply != null) { @@ -15388,17 +15405,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA; } parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; - if (hasNoCache) { + if (!drawingWithDrawingCache) { final int multipliedAlpha = (int) (255 * alpha); if (!onSetAlpha(multipliedAlpha)) { - int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; - if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 - || layerType != LAYER_TYPE_NONE) { - layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; - } if (drawingWithRenderNode) { renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha()); } else if (layerType == LAYER_TYPE_NONE) { + int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; + if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0) { + layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; + } canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha, layerFlags); } @@ -15433,33 +15449,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - if (hasNoCache) { - boolean layerRendered = false; - if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) { - final HardwareLayer layer = getHardwareLayer(); - if (layer != null && layer.isValid()) { - int restoreAlpha = mLayerPaint.getAlpha(); - mLayerPaint.setAlpha((int) (alpha * 255)); - ((DisplayListCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint); - mLayerPaint.setAlpha(restoreAlpha); - layerRendered = true; - } else { - canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint); - } - } - - if (!layerRendered) { - if (!drawingWithRenderNode) { - // Fast path for layouts with no backgrounds - if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { - mPrivateFlags &= ~PFLAG_DIRTY_MASK; - dispatchDraw(canvas); - } else { - draw(canvas); - } - } else { + if (!drawingWithDrawingCache) { + if (drawingWithRenderNode) { + mPrivateFlags &= ~PFLAG_DIRTY_MASK; + ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags); + } else { + // Fast path for layouts with no backgrounds + if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; - ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags); + dispatchDraw(canvas); + } else { + draw(canvas); } } } else if (cache != null) { @@ -18173,7 +18173,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { - throw new IllegalStateException("onMeasure() did not set the" + throw new IllegalStateException("View with id " + getId() + ": " + + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } @@ -20431,11 +20432,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static int adjust(int measureSpec, int delta) { final int mode = getMode(measureSpec); + int size = getSize(measureSpec); if (mode == UNSPECIFIED) { // No need to adjust size for UNSPECIFIED mode. - return makeMeasureSpec(0, UNSPECIFIED); + return makeMeasureSpec(size, UNSPECIFIED); } - int size = getSize(measureSpec) + delta; + size += delta; if (size < 0) { Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size + ") spec: " + toString(measureSpec) + " delta: " + delta); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 98b895d..8f2be99 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -494,6 +494,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ private int mNestedScrollAxes; + // Used to manage the list of transient views, added by addTransientView() + private List<Integer> mTransientIndices = null; + private List<View> mTransientViews = null; + + /** * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild. * @@ -2822,6 +2827,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.dispatchAttachedToWindow(info, visibility | (child.mViewFlags & VISIBILITY_MASK)); } + final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); + for (int i = 0; i < transientCount; ++i) { + View view = mTransientViews.get(i); + view.dispatchAttachedToWindow(info, visibility | (view.mViewFlags & VISIBILITY_MASK)); + } } @Override @@ -2991,6 +3001,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children[i].dispatchDetachedFromWindow(); } clearDisappearingChildren(); + final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size(); + for (int i = 0; i < transientCount; ++i) { + View view = mTransientViews.get(i); + view.dispatchDetachedFromWindow(); + } super.dispatchDetachedFromWindow(); } @@ -3291,6 +3306,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final long drawingTime = getDrawingTime(); if (usingRenderNodeProperties) canvas.insertReorderBarrier(); + final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); + int transientIndex = transientCount != 0 ? 0 : -1; // Only use the preordered list if not HW accelerated, since the HW pipeline will do the // draw reordering internally final ArrayList<View> preorderedList = usingRenderNodeProperties @@ -3298,6 +3315,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); for (int i = 0; i < childrenCount; i++) { + while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { + final View transientChild = mTransientViews.get(transientIndex); + if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || + transientChild.getAnimation() != null) { + more |= drawChild(canvas, transientChild, drawingTime); + } + transientIndex++; + if (transientIndex >= transientCount) { + transientIndex = -1; + } + } int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); @@ -3305,6 +3333,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager more |= drawChild(canvas, child, drawingTime); } } + while (transientIndex >= 0) { + // there may be additional transient views after the normal views + final View transientChild = mTransientViews.get(transientIndex); + if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || + transientChild.getAnimation() != null) { + more |= drawChild(canvas, transientChild, drawingTime); + } + transientIndex++; + if (transientIndex >= transientCount) { + break; + } + } if (preorderedList != null) preorderedList.clear(); // Draw any disappearing views that have animations @@ -3785,6 +3825,145 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * This method adds a view to this container at the specified index purely for the + * purposes of allowing that view to draw even though it is not a normal child of + * the container. That is, the view does not participate in layout, focus, accessibility, + * input, or other normal view operations; it is purely an item to be drawn during the normal + * rendering operation of this container. The index that it is added at is the order + * in which it will be drawn, with respect to the other views in the container. + * For example, a transient view added at index 0 will be drawn before all other views + * in the container because it will be drawn first (including before any real view + * at index 0). There can be more than one transient view at any particular index; + * these views will be drawn in the order in which they were added to the list of + * transient views. The index of transient views can also be greater than the number + * of normal views in the container; that just means that they will be drawn after all + * other views are drawn. + * + * <p>Note that since transient views do not participate in layout, they must be sized + * manually or, more typically, they should just use the size that they had before they + * were removed from their container.</p> + * + * <p>Transient views are useful for handling animations of views that have been removed + * from the container, but which should be animated out after the removal. Adding these + * views as transient views allows them to participate in drawing without side-effecting + * the layout of the container.</p> + * + * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed} + * from the container when they are no longer needed. For example, a transient view + * which is added in order to fade it out in its old location should be removed + * once the animation is complete.</p> + * + * @param view The view to be added + * @param index The index at which this view should be drawn, must be >= 0. + * This value is relative to the {@link #getChildAt(int) index} values in the normal + * child list of this container, where any transient view at a particular index will + * be drawn before any normal child at that same index. + * + * @hide + */ + public void addTransientView(View view, int index) { + if (index < 0) { + return; + } + if (mTransientIndices == null) { + mTransientIndices = new ArrayList<Integer>(); + mTransientViews = new ArrayList<View>(); + } + final int oldSize = mTransientIndices.size(); + if (oldSize > 0) { + int insertionIndex; + for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) { + if (index < mTransientIndices.get(insertionIndex)) { + break; + } + } + mTransientIndices.add(insertionIndex, index); + mTransientViews.add(insertionIndex, view); + } else { + mTransientIndices.add(index); + mTransientViews.add(view); + } + view.mParent = this; + view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); + invalidate(true); + } + + /** + * Removes a view from the list of transient views in this container. If there is no + * such transient view, this method does nothing. + * + * @param view The transient view to be removed + * + * @hide + */ + public void removeTransientView(View view) { + if (mTransientViews == null) { + return; + } + final int size = mTransientViews.size(); + for (int i = 0; i < size; ++i) { + if (view == mTransientViews.get(i)) { + mTransientViews.remove(i); + mTransientIndices.remove(i); + view.mParent = null; + view.dispatchDetachedFromWindow(); + invalidate(true); + return; + } + } + } + + /** + * Returns the number of transient views in this container. Specific transient + * views and the index at which they were added can be retrieved via + * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}. + * + * @see #addTransientView(View, int) + * @return The number of transient views in this container + * + * @hide + */ + public int getTransientViewCount() { + return mTransientIndices == null ? 0 : mTransientIndices.size(); + } + + /** + * Given a valid position within the list of transient views, returns the index of + * the transient view at that position. + * + * @param position The position of the index being queried. Must be at least 0 + * and less than the value returned by {@link #getTransientViewCount()}. + * @return The index of the transient view stored in the given position if the + * position is valid, otherwise -1 + * + * @hide + */ + public int getTransientViewIndex(int position) { + if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) { + return -1; + } + return mTransientIndices.get(position); + } + + /** + * Given a valid position within the list of transient views, returns the + * transient view at that position. + * + * @param position The position of the view being queried. Must be at least 0 + * and less than the value returned by {@link #getTransientViewCount()}. + * @return The transient view stored in the given position if the + * position is valid, otherwise null + * + * @hide + */ + public View getTransientView(int position) { + if (mTransientViews == null || position >= mTransientViews.size()) { + return null; + } + return mTransientViews.get(position); + } + + /** * <p>Adds a child view. If no layout parameters are already set on the child, the * default parameters for this ViewGroup are set on the child.</p> * @@ -4096,6 +4275,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } + + if (mTransientIndices != null) { + final int transientCount = mTransientIndices.size(); + for (int i = 0; i < transientCount; ++i) { + final int oldIndex = mTransientIndices.get(i); + if (index <= oldIndex) { + mTransientIndices.set(i, oldIndex + 1); + } + } + } } private void addInArray(View child, int index) { @@ -4340,6 +4529,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (view.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } + + int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); + for (int i = 0; i < transientCount; ++i) { + final int oldIndex = mTransientIndices.get(i); + if (index < oldIndex) { + mTransientIndices.set(i, oldIndex - 1); + } + } } /** @@ -6353,6 +6550,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public void onStopNestedScroll(View child) { // Stop any recursive nested scrolling. stopNestedScroll(); + mNestedScrollAxes = 0; } /** diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 4d8dce1..3340c73 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -76,6 +76,11 @@ public final class WebViewFactory { private static boolean sAddressSpaceReserved = false; private static PackageInfo sPackageInfo; + private static class MissingWebViewPackageException extends AndroidRuntimeException { + public MissingWebViewPackageException(String message) { super(message); } + public MissingWebViewPackageException(Exception e) { super(e); } + } + /** @hide */ public static String[] getWebViewPackageNames() { return AppGlobals.getInitialApplication().getResources().getStringArray( @@ -110,9 +115,10 @@ public final class WebViewFactory { } catch (PackageManager.NameNotFoundException e) { } } - throw new AndroidRuntimeException("Could not find a loadable WebView package"); + throw new MissingWebViewPackageException("Could not find a loadable WebView package"); } + // throws MissingWebViewPackageException private static ApplicationInfo getWebViewApplicationInfo() { if (sPackageInfo == null) return findPreferredWebViewPackage().applicationInfo; @@ -144,25 +150,7 @@ public final class WebViewFactory { Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()"); try { - // First fetch the package info so we can log the webview package version. - sPackageInfo = findPreferredWebViewPackage(); - Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " + - sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")"); - - Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()"); - loadNativeLibrary(); - Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); - - Class<WebViewFactoryProvider> providerClass; - Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getFactoryClass()"); - try { - providerClass = getFactoryClass(); - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "error loading provider", e); - throw new AndroidRuntimeException(e); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); - } + Class<WebViewFactoryProvider> providerClass = getProviderClass(); StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()"); @@ -184,7 +172,44 @@ public final class WebViewFactory { } } - private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException { + private static Class<WebViewFactoryProvider> getProviderClass() { + try { + // First fetch the package info so we can log the webview package version. + sPackageInfo = findPreferredWebViewPackage(); + Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " + + sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")"); + + Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()"); + loadNativeLibrary(); + Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); + + Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); + try { + return getChromiumProviderClass(); + } catch (ClassNotFoundException e) { + Log.e(LOGTAG, "error loading provider", e); + throw new AndroidRuntimeException(e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); + } + } catch (MissingWebViewPackageException e) { + // If the package doesn't exist, then try loading the null WebView instead. + // If that succeeds, then this is a device without WebView support; if it fails then + // swallow the failure, complain that the real WebView is missing and rethrow the + // original exception. + try { + return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); + } catch (ClassNotFoundException e2) { + // Ignore. + } + Log.e(LOGTAG, "Chromium WebView package does not exist", e); + throw new AndroidRuntimeException(e); + } + } + + // throws MissingWebViewPackageException + private static Class<WebViewFactoryProvider> getChromiumProviderClass() + throws ClassNotFoundException { Application initialApplication = AppGlobals.getInitialApplication(); try { // Construct a package context to load the Java code into the current app. @@ -202,17 +227,7 @@ public final class WebViewFactory { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } } catch (PackageManager.NameNotFoundException e) { - // If the package doesn't exist, then try loading the null WebView instead. - // If that succeeds, then this is a device without WebView support; if it fails then - // swallow the failure, complain that the real WebView is missing and rethrow the - // original exception. - try { - return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); - } catch (ClassNotFoundException e2) { - // Ignore. - } - Log.e(LOGTAG, "Chromium WebView package does not exist", e); - throw new AndroidRuntimeException(e); + throw new MissingWebViewPackageException(e); } } @@ -315,8 +330,8 @@ public final class WebViewFactory { prepareWebViewInSystemServer(nativeLibs); } - private static String[] getWebViewNativeLibraryPaths() - throws PackageManager.NameNotFoundException { + // throws MissingWebViewPackageException + private static String[] getWebViewNativeLibraryPaths() { ApplicationInfo ai = getWebViewApplicationInfo(); final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai); @@ -443,7 +458,7 @@ public final class WebViewFactory { } else if (DEBUG) { Log.v(LOGTAG, "loaded with relro file"); } - } catch (PackageManager.NameNotFoundException e) { + } catch (MissingWebViewPackageException e) { Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e); } } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index d6f9f78..ff74c60 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -703,9 +703,9 @@ public abstract class AbsSeekBar extends ProgressBar { // fallthrough case KeyEvent.KEYCODE_DPAD_RIGHT: increment = isLayoutRtl() ? -increment : increment; - int progress = getProgress() + increment; - if (progress > 0 && progress < getMax()) { - setProgress(progress, true); + + // Let progress bar handle clamping values. + if (setProgress(getProgress() + increment, true)) { onKeyChange(); return true; } @@ -729,10 +729,10 @@ public abstract class AbsSeekBar extends ProgressBar { if (isEnabled()) { final int progress = getProgress(); if (progress > 0) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); } if (progress < getMax()) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); } } } @@ -743,29 +743,26 @@ public abstract class AbsSeekBar extends ProgressBar { if (super.performAccessibilityActionInternal(action, arguments)) { return true; } + if (!isEnabled()) { return false; } - final int progress = getProgress(); - final int increment = Math.max(1, Math.round((float) getMax() / 5)); - switch (action) { - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { - if (progress <= 0) { - return false; - } - setProgress(progress - increment, true); - onKeyChange(); - return true; + + if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD + || action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { + int increment = Math.max(1, Math.round((float) getMax() / 5)); + if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { + increment = -increment; } - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { - if (progress >= getMax()) { - return false; - } - setProgress(progress + increment, true); + + // Let progress bar handle clamping values. + if (setProgress(getProgress() + increment, true)) { onKeyChange(); return true; } + return false; } + return false; } diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java index 7bce756..0ed75d5 100644 --- a/core/java/android/widget/CalendarViewMaterialDelegate.java +++ b/core/java/android/widget/CalendarViewMaterialDelegate.java @@ -19,6 +19,7 @@ package android.widget; import android.annotation.StyleRes; import android.content.Context; import android.util.AttributeSet; +import android.widget.DayPickerView.OnDaySelectedListener; import java.util.Calendar; @@ -109,8 +110,7 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele mOnDateChangeListener = listener; } - private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener = - new DayPickerView.OnDaySelectedListener() { + private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() { @Override public void onDaySelected(DayPickerView view, Calendar day) { if (mOnDateChangeListener != null) { diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 06a5bd2..d38a225 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -34,8 +34,6 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; import android.widget.DayPickerView.OnDaySelectedListener; import android.widget.YearPickerView.OnYearSelectedListener; @@ -549,7 +547,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { final int listPosition = ss.getListPosition(); if (listPosition != -1) { if (currentView == VIEW_MONTH_DAY) { - mDayPickerView.setCurrentItem(listPosition); + mDayPickerView.setPosition(listPosition); } else if (currentView == VIEW_YEAR) { final int listPositionOffset = ss.getListPositionOffset(); mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset); diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java index 9a4b6f5..478fa00 100644 --- a/core/java/android/widget/DayPickerAdapter.java +++ b/core/java/android/widget/DayPickerPagerAdapter.java @@ -36,7 +36,7 @@ import java.util.Calendar; /** * An adapter for a list of {@link android.widget.SimpleMonthView} items. */ -class DayPickerAdapter extends PagerAdapter { +class DayPickerPagerAdapter extends PagerAdapter { private static final int MONTHS_IN_YEAR = 12; private final Calendar mMinDate = Calendar.getInstance(); @@ -63,7 +63,7 @@ class DayPickerAdapter extends PagerAdapter { private int mCount; private int mFirstDayOfWeek; - public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId, + public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId, @IdRes int calendarViewId) { mInflater = LayoutInflater.from(context); mLayoutResId = layoutResId; @@ -200,7 +200,8 @@ class DayPickerAdapter extends PagerAdapter { final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR)); final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH)); - return yearOffset * MONTHS_IN_YEAR + monthOffset; + final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; + return position; } @Override @@ -253,8 +254,6 @@ class DayPickerAdapter extends PagerAdapter { v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); - v.setPrevEnabled(position > 0); - v.setNextEnabled(position < mCount - 1); final ViewHolder holder = new ViewHolder(position, itemView, v); mItems.put(position, holder); @@ -298,17 +297,10 @@ class DayPickerAdapter extends PagerAdapter { setSelectedDay(day); if (mOnDaySelectedListener != null) { - mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day); + mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day); } } } - - @Override - public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) { - if (mOnDaySelectedListener != null) { - mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate); - } - } }; private static class ViewHolder { @@ -324,7 +316,6 @@ class DayPickerAdapter extends PagerAdapter { } public interface OnDaySelectedListener { - public void onDaySelected(DayPickerAdapter view, Calendar day); - public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate); + public void onDaySelected(DayPickerPagerAdapter view, Calendar day); } } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index 0e0b2d3..c6b4d7e 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -16,37 +16,44 @@ package android.widget; -import com.android.internal.widget.ViewPager; import com.android.internal.R; +import com.android.internal.widget.ViewPager; +import com.android.internal.widget.ViewPager.OnPageChangeListener; +import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.MathUtils; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; -import java.util.ArrayList; import java.util.Calendar; import java.util.Locale; import libcore.icu.LocaleData; -/** - * This displays a list of months in a calendar format with selectable days. - */ -class DayPickerView extends ViewPager { +class DayPickerView extends ViewGroup { + private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material; private static final int DEFAULT_START_YEAR = 1900; private static final int DEFAULT_END_YEAR = 2100; + private static final int[] ATTRS_TEXT_COLOR = new int[] { R.attr.textColor }; + private final Calendar mSelectedDay = Calendar.getInstance(); private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); - private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); + private final AccessibilityManager mAccessibilityManager; + + private final ViewPager mViewPager; + private final ImageButton mPrevButton; + private final ImageButton mNextButton; - private final DayPickerAdapter mAdapter; + private final DayPickerPagerAdapter mAdapter; /** Temporary calendar used for date calculations. */ private Calendar mTempCalendar; @@ -57,17 +64,21 @@ class DayPickerView extends ViewPager { this(context, null); } - public DayPickerView(Context context, AttributeSet attrs) { + public DayPickerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, R.attr.calendarViewStyle); } - public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) { + public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes); @@ -93,14 +104,44 @@ class DayPickerView extends ViewPager { a.recycle(); // Set up adapter. - mAdapter = new DayPickerAdapter(context, + mAdapter = new DayPickerPagerAdapter(context, R.layout.date_picker_month_item_material, R.id.month_view); mAdapter.setMonthTextAppearance(monthTextAppearanceResId); mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId); mAdapter.setDayTextAppearance(dayTextAppearanceResId); mAdapter.setDaySelectorColor(daySelectorColor); - setAdapter(mAdapter); + final LayoutInflater inflater = LayoutInflater.from(context); + final ViewGroup content = (ViewGroup) inflater.inflate(DEFAULT_LAYOUT, this, false); + + // Transfer all children from content to here. + while (content.getChildCount() > 0) { + final View child = content.getChildAt(0); + content.removeViewAt(0); + addView(child); + } + + mPrevButton = (ImageButton) findViewById(R.id.prev); + mPrevButton.setOnClickListener(mOnClickListener); + + mNextButton = (ImageButton) findViewById(R.id.next); + mNextButton.setOnClickListener(mOnClickListener); + + mViewPager = (ViewPager) findViewById(R.id.day_picker_view_pager); + mViewPager.setAdapter(mAdapter); + mViewPager.setOnPageChangeListener(mOnPageChangedListener); + + // Proxy the month text color into the previous and next buttons. + if (monthTextAppearanceResId != 0) { + final TypedArray ta = mContext.obtainStyledAttributes(null, + ATTRS_TEXT_COLOR, 0, monthTextAppearanceResId); + final ColorStateList monthColor = ta.getColorStateList(0); + if (monthColor != null) { + mPrevButton.setImageTintList(monthColor); + mNextButton.setImageTintList(monthColor); + } + ta.recycle(); + } // Set up min and max dates. final Calendar tempDate = Calendar.getInstance(); @@ -127,109 +168,68 @@ class DayPickerView extends ViewPager { setDate(setDateMillis, false); // Proxy selection callbacks to our own listener. - mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() { + mAdapter.setOnDaySelectedListener(new DayPickerPagerAdapter.OnDaySelectedListener() { @Override - public void onDaySelected(DayPickerAdapter adapter, Calendar day) { + public void onDaySelected(DayPickerPagerAdapter adapter, Calendar day) { if (mOnDaySelectedListener != null) { mOnDaySelectedListener.onDaySelected(DayPickerView.this, day); } } - - @Override - public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) { - // ViewPager clamps input values, so we don't need to worry - // about passing invalid indices. - final int nextItem = getCurrentItem() + direction; - setCurrentItem(nextItem, animate); - } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - populate(); - - // Everything below is mostly copied from FrameLayout. - int count = getChildCount(); - - final boolean measureMatchParentChildren = - MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || - MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; - - int maxHeight = 0; - int maxWidth = 0; - int childState = 0; - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() != GONE) { - measureChild(child, widthMeasureSpec, heightMeasureSpec); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); - maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); - childState = combineMeasuredStates(childState, child.getMeasuredState()); - if (measureMatchParentChildren) { - if (lp.width == LayoutParams.MATCH_PARENT || - lp.height == LayoutParams.MATCH_PARENT) { - mMatchParentChildren.add(child); - } - } - } - } - - // Account for padding too - maxWidth += getPaddingLeft() + getPaddingRight(); - maxHeight += getPaddingTop() + getPaddingBottom(); - - // Check against our minimum height and width - maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); - maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); - - // Check against our foreground's minimum height and width - final Drawable drawable = getForeground(); - if (drawable != null) { - maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); - maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); - } - - setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), - resolveSizeAndState(maxHeight, heightMeasureSpec, - childState << MEASURED_HEIGHT_STATE_SHIFT)); - - count = mMatchParentChildren.size(); - if (count > 1) { - for (int i = 0; i < count; i++) { - final View child = mMatchParentChildren.get(i); - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final int childWidthMeasureSpec; - final int childHeightMeasureSpec; - - if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), - MeasureSpec.EXACTLY); - } else { - childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, - getPaddingLeft() + getPaddingRight(), - lp.width); - } - - if (lp.height == LayoutParams.MATCH_PARENT) { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), - MeasureSpec.EXACTLY); - } else { - childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, - getPaddingTop() + getPaddingBottom(), - lp.height); - } + final ViewPager viewPager = mViewPager; + measureChild(viewPager, widthMeasureSpec, heightMeasureSpec); + + final int measuredWidthAndState = viewPager.getMeasuredWidthAndState(); + final int measuredHeightAndState = viewPager.getMeasuredHeightAndState(); + setMeasuredDimension(measuredWidthAndState, measuredHeightAndState); + + final int pagerWidth = viewPager.getMeasuredWidth(); + final int pagerHeight = viewPager.getMeasuredHeight(); + final int buttonWidthSpec = MeasureSpec.makeMeasureSpec(pagerWidth, MeasureSpec.AT_MOST); + final int buttonHeightSpec = MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.AT_MOST); + mPrevButton.measure(buttonWidthSpec, buttonHeightSpec); + mNextButton.measure(buttonWidthSpec, buttonHeightSpec); + } - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final ImageButton leftButton = mPrevButton; + final ImageButton rightButton = mNextButton; + + final int width = right - left; + final int height = bottom - top; + mViewPager.layout(0, 0, width, height); + + if (mViewPager.getChildCount() < 1) { + leftButton.setVisibility(View.INVISIBLE); + rightButton.setVisibility(View.INVISIBLE); + return; } - mMatchParentChildren.clear(); + final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0); + final int monthHeight = monthView.getMonthHeight(); + final int cellWidth = monthView.getCellWidth(); + + // Vertically center the previous/next buttons within the month + // header, horizontally center within the day cell. + final int leftDW = leftButton.getMeasuredWidth(); + final int leftDH = leftButton.getMeasuredHeight(); + final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2; + final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2; + leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH); + leftButton.setVisibility(View.VISIBLE); + + final int rightDW = rightButton.getMeasuredWidth(); + final int rightDH = rightButton.getMeasuredHeight(); + final int rightIconTop = monthView.getPaddingTop() + (monthHeight - rightDH) / 2; + final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2; + rightButton.layout(rightIconRight - rightDW, rightIconTop, + rightIconRight, rightIconTop + rightDH); + rightButton.setVisibility(View.VISIBLE); } public void setDayOfWeekTextAppearance(int resId) { @@ -284,8 +284,8 @@ class DayPickerView extends ViewPager { } final int position = getPositionFromDay(timeInMillis); - if (position != getCurrentItem()) { - setCurrentItem(position, animate); + if (position != mViewPager.getCurrentItem()) { + mViewPager.setCurrentItem(position, animate); } mTempCalendar.setTimeInMillis(timeInMillis); @@ -365,10 +365,57 @@ class DayPickerView extends ViewPager { * Gets the position of the view that is most prominently displayed within the list view. */ public int getMostVisiblePosition() { - return getCurrentItem(); + return mViewPager.getCurrentItem(); } + public void setPosition(int position) { + mViewPager.setCurrentItem(position, false); + } + + private final OnPageChangeListener mOnPageChangedListener = new OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + final float alpha = Math.abs(0.5f - positionOffset) * 2.0f; + mPrevButton.setAlpha(alpha); + mNextButton.setAlpha(alpha); + } + + @Override + public void onPageScrollStateChanged(int state) {} + + @Override + public void onPageSelected(int position) { + mPrevButton.setVisibility( + position > 0 ? View.VISIBLE : View.INVISIBLE); + mNextButton.setVisibility( + position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE); + } + }; + + private final OnClickListener mOnClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + final int direction; + if (v == mPrevButton) { + direction = -1; + } else if (v == mNextButton) { + direction = 1; + } else { + return; + } + + // Animation is expensive for accessibility services since it sends + // lots of scroll and content change events. + final boolean animate = !mAccessibilityManager.isEnabled(); + + // ViewPager clamps input values, so we don't need to worry + // about passing invalid indices. + final int nextItem = mViewPager.getCurrentItem() + direction; + mViewPager.setCurrentItem(nextItem, animate); + } + }; + public interface OnDaySelectedListener { - public void onDaySelected(DayPickerView view, Calendar day); + void onDaySelected(DayPickerView view, Calendar day); } } diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java new file mode 100644 index 0000000..bb6e3a4 --- /dev/null +++ b/core/java/android/widget/DayPickerViewPager.java @@ -0,0 +1,137 @@ +/* + * 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.widget; + +import com.android.internal.widget.ViewPager; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; + +import java.util.ArrayList; + +/** + * This displays a list of months in a calendar format with selectable days. + */ +class DayPickerViewPager extends ViewPager { + private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); + + public DayPickerViewPager(Context context) { + this(context, null); + } + + public DayPickerViewPager(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + populate(); + + // Everything below is mostly copied from FrameLayout. + int count = getChildCount(); + + final boolean measureMatchParentChildren = + MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || + MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; + + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + measureChild(child, widthMeasureSpec, heightMeasureSpec); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); + maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + if (measureMatchParentChildren) { + if (lp.width == LayoutParams.MATCH_PARENT || + lp.height == LayoutParams.MATCH_PARENT) { + mMatchParentChildren.add(child); + } + } + } + } + + // Account for padding too + maxWidth += getPaddingLeft() + getPaddingRight(); + maxHeight += getPaddingTop() + getPaddingBottom(); + + // Check against our minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + // Check against our foreground's minimum height and width + final Drawable drawable = getForeground(); + if (drawable != null) { + maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); + maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); + } + + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + + count = mMatchParentChildren.size(); + if (count > 1) { + for (int i = 0; i < count; i++) { + final View child = mMatchParentChildren.get(i); + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int childWidthMeasureSpec; + final int childHeightMeasureSpec; + + if (lp.width == LayoutParams.MATCH_PARENT) { + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + MeasureSpec.EXACTLY); + } else { + childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, + getPaddingLeft() + getPaddingRight(), + lp.width); + } + + if (lp.height == LayoutParams.MATCH_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, + getPaddingTop() + getPaddingBottom(), + lp.height); + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + } + + mMatchParentChildren.clear(); + } +} diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 4b5407a..552b274 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -662,7 +662,8 @@ class FastScroller { final int adjMaxWidth = maxWidth - marginLeft - marginRight; final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(), + MeasureSpec.UNSPECIFIED); view.measure(widthMeasureSpec, heightMeasureSpec); // Align to the left or right. @@ -701,7 +702,8 @@ class FastScroller { final int containerWidth = container.width(); final int adjMaxWidth = containerWidth - marginLeft - marginRight; final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(), + MeasureSpec.UNSPECIFIED); preview.measure(widthMeasureSpec, heightMeasureSpec); // Align at the vertical center, 10% from the top. @@ -766,7 +768,8 @@ class FastScroller { final Rect container = mContainerRect; final int maxWidth = container.width(); final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(), + MeasureSpec.UNSPECIFIED); track.measure(widthMeasureSpec, heightMeasureSpec); final int top; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index be0ca71..9ecdc9c 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1071,7 +1071,8 @@ public class GridView extends AbsListView { p.forceAdd = true; int childHeightSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), + MeasureSpec.UNSPECIFIED), 0, p.height); int childWidthSpec = getChildMeasureSpec( MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); child.measure(childWidthSpec, childHeightSpec); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index da15302..72f51c9 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1058,8 +1058,11 @@ public class LinearLayout extends ViewGroup { // use as much space as it wants because we can shrink things // later (and re-measure). if (baselineAligned) { - final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - child.measure(freeSpec, freeSpec); + final int freeWidthSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED); + final int freeHeightSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED); + child.measure(freeWidthSpec, freeHeightSpec); } else { skippedMeasure = true; } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index def5929..a79c8e8 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1155,7 +1155,7 @@ public class ListView extends AbsListView { heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); - measureScrapChild(child, 0, widthMeasureSpec); + measureScrapChild(child, 0, widthMeasureSpec, heightSize); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); @@ -1188,7 +1188,7 @@ public class ListView extends AbsListView { mWidthMeasureSpec = widthMeasureSpec; } - private void measureScrapChild(View child, int position, int widthMeasureSpec) { + private void measureScrapChild(View child, int position, int widthMeasureSpec, int heightHint) { LayoutParams p = (LayoutParams) child.getLayoutParams(); if (p == null) { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); @@ -1204,7 +1204,7 @@ public class ListView extends AbsListView { if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + childHeightSpec = MeasureSpec.makeMeasureSpec(heightHint, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } @@ -1271,7 +1271,7 @@ public class ListView extends AbsListView { for (i = startPosition; i <= endPosition; ++i) { child = obtainView(i, isScrap); - measureScrapChild(child, i, widthMeasureSpec); + measureScrapChild(child, i, widthMeasureSpec, maxHeight); if (i > 0) { // Count the divider for all but one child @@ -1941,7 +1941,8 @@ public class ListView extends AbsListView { if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + childHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), + MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } else { @@ -2695,7 +2696,8 @@ public class ListView extends AbsListView { if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + childHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), + MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index c5b5c84..c3ac278 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -94,7 +94,6 @@ public class PopupWindow { private final int[] mDrawingLocation = new int[2]; private final int[] mScreenLocation = new int[2]; private final Rect mTempRect = new Rect(); - private final Rect mAnchorBounds = new Rect(); private Context mContext; private WindowManager mWindowManager; @@ -159,28 +158,6 @@ public class PopupWindow { private WeakReference<View> mAnchor; - private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() { - @Override - public Rect onGetEpicenter(Transition transition) { - final View anchor = mAnchor != null ? mAnchor.get() : null; - final View decor = mDecorView; - if (anchor == null || decor == null) { - return null; - } - - final Rect anchorBounds = mAnchorBounds; - final int[] anchorLocation = anchor.getLocationOnScreen(); - final int[] popupLocation = mDecorView.getLocationOnScreen(); - - // Compute the position of the anchor relative to the popup. - anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight()); - anchorBounds.offset(anchorLocation[0] - popupLocation[0], - anchorLocation[1] - popupLocation[1]); - - return anchorBounds; - } - }; - private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override public void onScrollChanged() { @@ -355,18 +332,10 @@ public class PopupWindow { public void setEnterTransition(Transition enterTransition) { mEnterTransition = enterTransition; - - if (mEnterTransition != null) { - mEnterTransition.setEpicenterCallback(mEpicenterCallback); - } } public void setExitTransition(Transition exitTransition) { mExitTransition = exitTransition; - - if (mExitTransition != null) { - mExitTransition.setEpicenterCallback(mEpicenterCallback); - } } private Transition getTransition(int resId) { @@ -1281,11 +1250,14 @@ public class PopupWindow { final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor); - decorView.requestEnterTransition(mEnterTransition); setLayoutDirectionFromAnchor(); mWindowManager.addView(decorView, p); + + if (mEnterTransition != null) { + decorView.requestEnterTransition(mEnterTransition); + } } private void setLayoutDirectionFromAnchor() { @@ -1607,13 +1579,25 @@ public class PopupWindow { // Ensure any ongoing or pending transitions are canceled. decorView.cancelTransitions(); - unregisterForScrollChanged(); - mIsShowing = false; mIsTransitioningToDismiss = true; - if (mExitTransition != null && decorView.isLaidOut()) { - decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() { + final Transition exitTransition = mExitTransition; + if (exitTransition != null && decorView.isLaidOut()) { + // The decor view is non-interactive during exit transitions. + final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); + p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; + p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; + mWindowManager.updateViewLayout(decorView, p); + + final Rect epicenter = getRelativeAnchorBounds(); + exitTransition.setEpicenterCallback(new EpicenterCallback() { + @Override + public Rect onGetEpicenter(Transition transition) { + return epicenter; + } + }); + decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); @@ -1623,11 +1607,30 @@ public class PopupWindow { dismissImmediate(decorView, contentHolder, contentView); } + // Clears the anchor view. + unregisterForScrollChanged(); + if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); } } + private Rect getRelativeAnchorBounds() { + final View anchor = mAnchor != null ? mAnchor.get() : null; + final View decor = mDecorView; + if (anchor == null || decor == null) { + return null; + } + + final int[] anchorLocation = anchor.getLocationOnScreen(); + final int[] popupLocation = mDecorView.getLocationOnScreen(); + + // Compute the position of the anchor relative to the popup. + final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight()); + bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); + return bounds; + } + /** * Removes the popup from the window manager and tears down the supporting * view hierarchy, if necessary. @@ -1996,6 +1999,13 @@ public class PopupWindow { observer.removeOnGlobalLayoutListener(this); } + final Rect epicenter = getRelativeAnchorBounds(); + enterTransition.setEpicenterCallback(new EpicenterCallback() { + @Override + public Rect onGetEpicenter(Transition transition) { + return epicenter; + } + }); startEnterTransition(enterTransition); } }); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 24e9cbe..b59ae17 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -43,6 +43,7 @@ import android.graphics.drawable.shapes.Shape; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; +import android.util.MathUtils; import android.util.Pools.SynchronizedPool; import android.view.Gravity; import android.view.RemotableViewMethod; @@ -1341,23 +1342,22 @@ public class ProgressBar extends View { } @android.view.RemotableViewMethod - synchronized void setProgress(int progress, boolean fromUser) { + synchronized boolean setProgress(int progress, boolean fromUser) { if (mIndeterminate) { - return; + // Not applicable. + return false; } - if (progress < 0) { - progress = 0; - } + progress = MathUtils.constrain(progress, 0, mMax); - if (progress > mMax) { - progress = mMax; + if (progress == mProgress) { + // No change from current. + return false; } - if (progress != mProgress) { - mProgress = progress; - refreshProgress(R.id.progress, mProgress, fromUser); - } + mProgress = progress; + refreshProgress(R.id.progress, mProgress, fromUser); + return true; } /** diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index b95c27d..2026169 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1238,7 +1238,8 @@ public class ScrollView extends FrameLayout { } @Override - protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { + protected void measureChild(View child, int parentWidthMeasureSpec, + int parentHeightMeasureSpec) { ViewGroup.LayoutParams lp = child.getLayoutParams(); int childWidthMeasureSpec; @@ -1247,7 +1248,8 @@ public class ScrollView extends FrameLayout { childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } @@ -1261,7 +1263,7 @@ public class ScrollView extends FrameLayout { mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); + MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index aa7f0b6..0249c22 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -26,7 +26,6 @@ import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextPaint; import android.text.format.DateFormat; @@ -60,12 +59,6 @@ class SimpleMonthView extends View { private static final String DEFAULT_TITLE_FORMAT = "MMMMy"; private static final String DAY_OF_WEEK_FORMAT = "EEEEE"; - /** Virtual view ID for previous button. */ - private static final int ITEM_ID_PREV = 0x101; - - /** Virtual view ID for next button. */ - private static final int ITEM_ID_NEXT = 0x100; - private final TextPaint mMonthPaint = new TextPaint(); private final TextPaint mDayOfWeekPaint = new TextPaint(); private final TextPaint mDayPaint = new TextPaint(); @@ -87,14 +80,6 @@ class SimpleMonthView extends View { private final int mDesiredCellWidth; private final int mDesiredDaySelectorRadius; - // Next/previous drawables. - private final Drawable mPrevDrawable; - private final Drawable mNextDrawable; - private final Rect mPrevHitArea; - private final Rect mNextHitArea; - private final CharSequence mPrevContentDesc; - private final CharSequence mNextContentDesc; - private CharSequence mTitle; private int mMonth; @@ -137,9 +122,6 @@ class SimpleMonthView extends View { /** The day of month for the last (inclusive) enabled day. */ private int mEnabledDayEnd = 31; - /** The number of week rows needed to display the current month. */ - private int mNumWeeks = MAX_WEEKS_IN_MONTH; - /** Optional listener for handling day click actions. */ private OnDayClickListener mOnDayClickListener; @@ -147,9 +129,6 @@ class SimpleMonthView extends View { private int mTouchedItem = -1; - private boolean mPrevEnabled; - private boolean mNextEnabled; - public SimpleMonthView(Context context) { this(context, null); } @@ -170,14 +149,8 @@ class SimpleMonthView extends View { mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); - mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); - - mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left); - mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right); - mPrevHitArea = mPrevDrawable != null ? new Rect() : null; - mNextHitArea = mNextDrawable != null ? new Rect() : null; - mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button); - mNextContentDesc = res.getText(R.string.date_picker_next_month_button); + mDesiredDaySelectorRadius = res.getDimensionPixelSize( + R.dimen.date_picker_day_selector_radius); // Set up accessibility components. mTouchHelper = new MonthViewTouchHelper(this); @@ -193,18 +166,6 @@ class SimpleMonthView extends View { initPaints(res); } - public void setNextEnabled(boolean enabled) { - mNextEnabled = enabled; - mTouchHelper.invalidateRoot(); - invalidate(); - } - - public void setPrevEnabled(boolean enabled) { - mPrevEnabled = enabled; - mTouchHelper.invalidateRoot(); - invalidate(); - } - /** * Applies the specified text appearance resource to a paint, returning the * text color if one is set in the text appearance. @@ -236,16 +197,16 @@ class SimpleMonthView extends View { return textColor; } + public int getMonthHeight() { + return mMonthHeight; + } + + public int getCellWidth() { + return mCellWidth; + } + public void setMonthTextAppearance(int resId) { - final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId); - if (monthColor != null) { - if (mPrevDrawable != null) { - mPrevDrawable.setTintList(monthColor); - } - if (mNextDrawable != null) { - mNextDrawable.setTintList(monthColor); - } - } + applyTextAppearance(mMonthPaint, resId); invalidate(); } @@ -360,7 +321,7 @@ class SimpleMonthView extends View { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: - final int touchedItem = getItemAtLocation(x, y); + final int touchedItem = getDayAtLocation(x, y); if (mTouchedItem != touchedItem) { mTouchedItem = touchedItem; invalidate(); @@ -368,8 +329,8 @@ class SimpleMonthView extends View { break; case MotionEvent.ACTION_UP: - final int clickedItem = getItemAtLocation(x, y); - onItemClicked(clickedItem, true); + final int clickedDay = getDayAtLocation(x, y); + onDayClicked(clickedDay); // Fall through. case MotionEvent.ACTION_CANCEL: // Reset touched day on stream end. @@ -389,7 +350,6 @@ class SimpleMonthView extends View { drawMonth(canvas); drawDaysOfWeek(canvas); drawDays(canvas); - drawButtons(canvas); canvas.translate(-paddingLeft, -paddingTop); } @@ -482,16 +442,6 @@ class SimpleMonthView extends View { } } - private void drawButtons(Canvas canvas) { - if (mPrevEnabled && mPrevDrawable != null) { - mPrevDrawable.draw(canvas); - } - - if (mNextEnabled && mNextDrawable != null) { - mNextDrawable.draw(canvas); - } - } - private static boolean isValidDayOfWeek(int day) { return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; } @@ -674,33 +624,6 @@ class SimpleMonthView extends View { mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, Math.min(maxSelectorWidth, maxSelectorHeight)); - // Vertically center the previous/next drawables within the month - // header, horizontally center within the day cell, then expand the - // hit area to ensure it's at least 48x48dp. - final Drawable prevDrawable = mPrevDrawable; - if (prevDrawable != null) { - final int dW = prevDrawable.getIntrinsicWidth(); - final int dH = prevDrawable.getIntrinsicHeight(); - final int iconTop = (monthHeight - dH) / 2; - final int iconLeft = (cellWidth - dW) / 2; - - // Button bounds don't include padding, but hit area does. - prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH); - mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight); - } - - final Drawable nextDrawable = mNextDrawable; - if (nextDrawable != null) { - final int dW = nextDrawable.getIntrinsicWidth(); - final int dH = nextDrawable.getIntrinsicHeight(); - final int iconTop = (monthHeight - dH) / 2; - final int iconRight = paddedWidth - (cellWidth - dW) / 2; - - // Button bounds don't include padding, but hit area does. - nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH); - mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight); - } - // Invalidate cached accessibility information. mTouchHelper.invalidateRoot(); } @@ -714,22 +637,15 @@ class SimpleMonthView extends View { } /** - * Calculates the day of the month or item identifier at the specified - * touch position. Returns the day of the month or -1 if the position - * wasn't in a valid day. + * Calculates the day of the month at the specified touch position. Returns + * the day of the month or -1 if the position wasn't in a valid day. * * @param x the x position of the touch event * @param y the y position of the touch event - * @return the day of the month at (x, y), an item identifier, or -1 if the - * position wasn't in a valid day or item + * @return the day of the month at (x, y), or -1 if the position wasn't in + * a valid day */ - private int getItemAtLocation(int x, int y) { - if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) { - return ITEM_ID_NEXT; - } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) { - return ITEM_ID_PREV; - } - + private int getDayAtLocation(int x, int y) { final int paddedX = x - getPaddingLeft(); if (paddedX < 0 || paddedX >= mPaddedWidth) { return -1; @@ -755,22 +671,10 @@ class SimpleMonthView extends View { /** * Calculates the bounds of the specified day. * - * @param id the day of the month, or an item identifier + * @param id the day of the month * @param outBounds the rect to populate with bounds */ - private boolean getBoundsForItem(int id, Rect outBounds) { - if (mNextEnabled && id == ITEM_ID_NEXT) { - if (mNextDrawable != null) { - outBounds.set(mNextHitArea); - return true; - } - } else if (mPrevEnabled && id == ITEM_ID_PREV) { - if (mPrevDrawable != null) { - outBounds.set(mPrevHitArea); - return true; - } - } - + private boolean getBoundsForDay(int id, Rect outBounds) { if (id < 1 || id > mDaysInMonth) { return false; } @@ -789,16 +693,8 @@ class SimpleMonthView extends View { final int top = getPaddingTop() + headerHeight + row * rowHeight; outBounds.set(left, top, left + colWidth, top + rowHeight); - return true; - } - /** - * Called when an item is clicked. - * - * @param id the day number or item identifier - */ - private boolean onItemClicked(int id, boolean animate) { - return onNavigationClicked(id, animate) || onDayClicked(id); + return true; } /** @@ -824,31 +720,6 @@ class SimpleMonthView extends View { } /** - * Called when the user clicks on a navigation button. Handles callbacks to - * the {@link OnDayClickListener} if one is set. - * - * @param id the item identifier - */ - private boolean onNavigationClicked(int id, boolean animate) { - final int direction; - if (id == ITEM_ID_NEXT) { - direction = 1; - } else if (id == ITEM_ID_PREV) { - direction = -1; - } else { - return false; - } - - if (mOnDayClickListener != null) { - mOnDayClickListener.onNavigationClick(this, direction, animate); - } - - // This is a no-op if accessibility is turned off. - mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED); - return true; - } - - /** * Provides a virtual view hierarchy for interfacing with an accessibility * service. */ @@ -864,7 +735,7 @@ class SimpleMonthView extends View { @Override protected int getVirtualViewAt(float x, float y) { - final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f)); + final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f)); if (day >= 0) { return day; } @@ -873,14 +744,6 @@ class SimpleMonthView extends View { @Override protected void getVisibleVirtualViews(IntArray virtualViewIds) { - if (mNextEnabled && mNextDrawable != null) { - virtualViewIds.add(ITEM_ID_PREV); - } - - if (mPrevEnabled && mPrevDrawable != null) { - virtualViewIds.add(ITEM_ID_NEXT); - } - for (int day = 1; day <= mDaysInMonth; day++) { virtualViewIds.add(day); } @@ -888,12 +751,12 @@ class SimpleMonthView extends View { @Override protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { - event.setContentDescription(getItemDescription(virtualViewId)); + event.setContentDescription(getDayDescription(virtualViewId)); } @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) { - final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect); + final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect); if (!hasBounds) { // The day is invalid, kill the node. @@ -904,8 +767,8 @@ class SimpleMonthView extends View { return; } - node.setText(getItemText(virtualViewId)); - node.setContentDescription(getItemDescription(virtualViewId)); + node.setText(getDayText(virtualViewId)); + node.setContentDescription(getDayDescription(virtualViewId)); node.setBoundsInParent(mTempRect); node.addAction(AccessibilityAction.ACTION_CLICK); @@ -921,7 +784,7 @@ class SimpleMonthView extends View { Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: - return onItemClicked(virtualViewId, false); + return onDayClicked(virtualViewId); } return false; @@ -930,15 +793,11 @@ class SimpleMonthView extends View { /** * Generates a description for a given virtual view. * - * @param id the day or item identifier to generate a description for + * @param id the day to generate a description for * @return a description of the virtual view */ - private CharSequence getItemDescription(int id) { - if (id == ITEM_ID_NEXT) { - return mNextContentDesc; - } else if (id == ITEM_ID_PREV) { - return mPrevContentDesc; - } else if (id >= 1 && id <= mDaysInMonth) { + private CharSequence getDayDescription(int id) { + if (id >= 1 && id <= mDaysInMonth) { mTempCalendar.set(mYear, mMonth, id); return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis()); } @@ -949,13 +808,11 @@ class SimpleMonthView extends View { /** * Generates displayed text for a given virtual view. * - * @param id the day or item identifier to generate text for + * @param id the day to generate text for * @return the visible text of the virtual view */ - private CharSequence getItemText(int id) { - if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) { - return null; - } else if (id >= 1 && id <= mDaysInMonth) { + private CharSequence getDayText(int id) { + if (id >= 1 && id <= mDaysInMonth) { return Integer.toString(id); } @@ -967,7 +824,6 @@ class SimpleMonthView extends View { * Handles callbacks when the user clicks on a time object. */ public interface OnDayClickListener { - public void onDayClick(SimpleMonthView view, Calendar day); - public void onNavigationClick(SimpleMonthView view, int direction, boolean animate); + void onDayClick(SimpleMonthView view, Calendar day); } } diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 3746ec6..095cc44 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -811,9 +811,9 @@ public class Spinner extends AbsSpinner implements OnClickListener { View itemView = null; int itemType = 0; final int widthMeasureSpec = - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED); final int heightMeasureSpec = - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.UNSPECIFIED); // Make sure the number of items we'll measure is capped. If it's a huge data set // with wildly varying sizes, oh well. diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 9496e62..aa7168c 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -173,11 +173,12 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { } // First, measure with no constraint - final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); mImposedTabsHeight = -1; super.measureHorizontal(unspecifiedWidth, heightMeasureSpec); - int extraWidth = getMeasuredWidth() - MeasureSpec.getSize(widthMeasureSpec); + int extraWidth = getMeasuredWidth() - width; if (extraWidth > 0) { final int count = getChildCount(); diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index 093bdcf..6fdd874 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -467,7 +467,7 @@ public class TableLayout extends LinearLayout { */ @Override void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { - findLargestCells(widthMeasureSpec); + findLargestCells(widthMeasureSpec, heightMeasureSpec); shrinkAndStretchColumns(widthMeasureSpec); super.measureVertical(widthMeasureSpec, heightMeasureSpec); @@ -479,7 +479,7 @@ public class TableLayout extends LinearLayout { * * @param widthMeasureSpec the measure constraint imposed by our parent */ - private void findLargestCells(int widthMeasureSpec) { + private void findLargestCells(int widthMeasureSpec, int heightMeasureSpec) { boolean firstRow = true; // find the maximum width for each column @@ -502,7 +502,7 @@ public class TableLayout extends LinearLayout { final ViewGroup.LayoutParams layoutParams = row.getLayoutParams(); layoutParams.height = LayoutParams.WRAP_CONTENT; - final int[] widths = row.getColumnsWidths(widthMeasureSpec); + final int[] widths = row.getColumnsWidths(widthMeasureSpec, heightMeasureSpec); final int newLength = widths.length; // this is the first row, we just need to copy the values if (firstRow) { diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index faf5b84..f73ee49 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -283,7 +283,7 @@ public class TableRow extends LinearLayout { * column, in this row * {@hide} */ - int[] getColumnsWidths(int widthMeasureSpec) { + int[] getColumnsWidths(int widthMeasureSpec, int heightMeasureSpec) { final int numColumns = getVirtualChildCount(); if (mColumnWidths == null || numColumns != mColumnWidths.length) { mColumnWidths = new int[numColumns]; @@ -302,7 +302,9 @@ public class TableRow extends LinearLayout { spec = getChildMeasureSpec(widthMeasureSpec, 0, LayoutParams.WRAP_CONTENT); break; case LayoutParams.MATCH_PARENT: - spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + spec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(heightMeasureSpec), + MeasureSpec.UNSPECIFIED); break; default: spec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7328300..726b89a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1460,7 +1460,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isTextEditable()) { replaceSelectionWithText(result); } else { - Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show(); + if (result.length() > 0) { + Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show(); + } } } } |