summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/Activity.java123
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/ActivityView.java7
-rw-r--r--core/java/android/app/ApplicationPackageManager.java15
-rw-r--r--core/java/android/app/AssistStructure.java305
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/Fragment.java101
-rw-r--r--core/java/android/app/INotificationManager.aidl3
-rw-r--r--core/java/android/app/Notification.java494
-rw-r--r--core/java/android/app/SystemServiceRegistry.java8
-rw-r--r--core/java/android/app/UiAutomationConnection.java53
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java253
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java9
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java233
-rw-r--r--core/java/android/app/usage/NetworkUsageStats.java479
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java9
-rw-r--r--core/java/android/content/Context.java34
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/Intent.java74
-rw-r--r--core/java/android/content/UndoManager.java29
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java29
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl44
-rw-r--r--core/java/android/content/pm/PackageInfo.java5
-rw-r--r--core/java/android/content/pm/PackageManager.java153
-rw-r--r--core/java/android/content/pm/PackageParser.java77
-rw-r--r--core/java/android/content/res/ColorStateList.java27
-rw-r--r--core/java/android/content/res/Resources.java55
-rw-r--r--core/java/android/content/res/StringBlock.java2
-rw-r--r--core/java/android/hardware/ICameraService.aidl8
-rw-r--r--core/java/android/hardware/SystemSensorManager.java27
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java6
-rw-r--r--core/java/android/hardware/camera2/ICameraDeviceUser.aidl4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java46
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java9
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java6
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java2
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java4
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.aidl (renamed from core/java/android/hardware/IProCameraCallbacks.aidl)12
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java53
-rw-r--r--core/java/android/hardware/display/DisplayManager.java3
-rw-r--r--core/java/android/hardware/hdmi/HdmiPlaybackClient.java14
-rw-r--r--core/java/android/hardware/hdmi/HdmiTvClient.java13
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareImpl.java2
-rw-r--r--core/java/android/net/BaseDhcpStateMachine.java35
-rw-r--r--core/java/android/net/ConnectivityManager.java2
-rw-r--r--core/java/android/net/DhcpStateMachine.java4
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl3
-rw-r--r--core/java/android/net/INetworkStatsService.aidl8
-rw-r--r--core/java/android/net/INetworkStatsSession.aidl6
-rw-r--r--core/java/android/net/NetworkCapabilities.java4
-rw-r--r--core/java/android/net/NetworkUtils.java51
-rw-r--r--core/java/android/net/dhcp/DhcpAckPacket.java109
-rw-r--r--core/java/android/net/dhcp/DhcpDeclinePacket.java65
-rw-r--r--core/java/android/net/dhcp/DhcpDiscoverPacket.java71
-rw-r--r--core/java/android/net/dhcp/DhcpInformPacket.java76
-rw-r--r--core/java/android/net/dhcp/DhcpNakPacket.java72
-rw-r--r--core/java/android/net/dhcp/DhcpOfferPacket.java100
-rw-r--r--core/java/android/net/dhcp/DhcpPacket.java895
-rw-r--r--core/java/android/net/dhcp/DhcpRequestPacket.java86
-rw-r--r--core/java/android/net/dhcp/DhcpStateMachine.java74
-rw-r--r--core/java/android/os/BatteryStats.java1088
-rw-r--r--core/java/android/os/IPowerManager.aidl2
-rw-r--r--core/java/android/os/PowerManager.java57
-rw-r--r--core/java/android/os/PowerManagerInternal.java4
-rw-r--r--core/java/android/os/Process.java3
-rw-r--r--core/java/android/os/UserManager.java12
-rw-r--r--core/java/android/os/storage/IMountService.java25
-rw-r--r--core/java/android/preference/Preference.java6
-rw-r--r--core/java/android/print/PrintDocumentInfo.java2
-rw-r--r--core/java/android/provider/ContactsContract.java57
-rw-r--r--core/java/android/provider/Settings.java9
-rw-r--r--core/java/android/security/IKeystoreService.aidl8
-rw-r--r--core/java/android/security/NetworkSecurityPolicy.java75
-rw-r--r--core/java/android/security/keymaster/KeymasterBlob.aidl20
-rw-r--r--core/java/android/security/keymaster/KeymasterBlob.java55
-rw-r--r--core/java/android/security/keymaster/KeymasterBlobArgument.java7
-rw-r--r--core/java/android/security/keymaster/KeymasterBooleanArgument.java6
-rw-r--r--core/java/android/security/keymaster/KeymasterDateArgument.java6
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java9
-rw-r--r--core/java/android/security/keymaster/KeymasterIntArgument.java9
-rw-r--r--core/java/android/security/keymaster/KeymasterLongArgument.java6
-rw-r--r--core/java/android/service/fingerprint/Fingerprint.aidl19
-rw-r--r--core/java/android/service/fingerprint/Fingerprint.java93
-rw-r--r--core/java/android/service/fingerprint/FingerprintManager.java566
-rw-r--r--core/java/android/service/fingerprint/FingerprintManagerReceiver.java76
-rw-r--r--core/java/android/service/fingerprint/FingerprintUtils.java2
-rw-r--r--core/java/android/service/fingerprint/IFingerprintService.aidl37
-rw-r--r--core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl10
-rw-r--r--core/java/android/service/voice/IVoiceInteractionSession.aidl2
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java8
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java21
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java42
-rw-r--r--core/java/android/text/Html.java18
-rw-r--r--core/java/android/text/Layout.java5
-rw-r--r--core/java/android/text/SpannableStringBuilder.java19
-rw-r--r--core/java/android/text/StaticLayout.java55
-rw-r--r--core/java/android/text/style/URLSpan.java8
-rw-r--r--core/java/android/text/util/Linkify.java4
-rw-r--r--core/java/android/util/IntArray.java26
-rw-r--r--core/java/android/util/PathParser.java2
-rw-r--r--core/java/android/view/DisplayListCanvas.java62
-rw-r--r--core/java/android/view/GhostView.java10
-rw-r--r--core/java/android/view/HardwareCanvas.java124
-rw-r--r--core/java/android/view/HardwareRenderer.java4
-rw-r--r--core/java/android/view/IWindowManager.aidl9
-rw-r--r--core/java/android/view/InputDevice.java26
-rw-r--r--core/java/android/view/LayoutInflater.java183
-rw-r--r--core/java/android/view/PhoneFallbackEventHandler.java100
-rw-r--r--core/java/android/view/PhoneWindow.java41
-rw-r--r--core/java/android/view/RenderNode.java38
-rw-r--r--core/java/android/view/Surface.java2
-rw-r--r--core/java/android/view/TextureView.java6
-rw-r--r--core/java/android/view/ThreadedRenderer.java2
-rw-r--r--core/java/android/view/View.java369
-rw-r--r--core/java/android/view/ViewRootImpl.java33
-rw-r--r--core/java/android/view/WindowManagerGlobal.java10
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java12
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java4
-rw-r--r--core/java/android/webkit/WebChromeClient.java8
-rw-r--r--core/java/android/webkit/WebView.java67
-rw-r--r--core/java/android/webkit/WebViewDelegate.java10
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java8
-rw-r--r--core/java/android/widget/CalendarView.java199
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java9
-rw-r--r--core/java/android/widget/CalendarViewMaterialDelegate.java160
-rw-r--r--core/java/android/widget/DatePicker.java47
-rwxr-xr-xcore/java/android/widget/DatePickerCalendarDelegate.java504
-rw-r--r--core/java/android/widget/DayPickerAdapter.java283
-rw-r--r--core/java/android/widget/DayPickerView.java560
-rw-r--r--core/java/android/widget/Editor.java326
-rw-r--r--core/java/android/widget/FastScroller.java6
-rw-r--r--core/java/android/widget/FrameLayout.java326
-rw-r--r--core/java/android/widget/Gallery.java20
-rw-r--r--core/java/android/widget/ListPopupWindow.java35
-rw-r--r--core/java/android/widget/PopupWindow.java123
-rw-r--r--core/java/android/widget/RadialTimePickerView.java25
-rw-r--r--core/java/android/widget/SimpleMonthAdapter.java220
-rw-r--r--core/java/android/widget/SimpleMonthView.java695
-rw-r--r--core/java/android/widget/SuggestionsAdapter.java2
-rw-r--r--core/java/android/widget/TabHost.java4
-rw-r--r--core/java/android/widget/TextView.java138
-rw-r--r--core/java/android/widget/TextViewWithCircularIndicator.java87
-rw-r--r--core/java/android/widget/YearPickerView.java245
-rw-r--r--core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl (renamed from core/java/android/hardware/IProCameraUser.aidl)14
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl1
-rw-r--r--core/java/com/android/internal/app/WindowDecorActionBar.java1
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java31
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java72
-rw-r--r--core/java/com/android/internal/logging/EventLogTags.logtags7
-rw-r--r--core/java/com/android/internal/logging/MetricsConstants.java153
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java47
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java1254
-rw-r--r--core/java/com/android/internal/os/KernelWakelockReader.java192
-rw-r--r--core/java/com/android/internal/os/KernelWakelockStats.java37
-rw-r--r--core/java/com/android/internal/os/Zygote.java5
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java4
-rw-r--r--core/java/com/android/internal/util/Protocol.java1
-rw-r--r--core/java/com/android/internal/util/ScreenShapeHelper.java48
-rw-r--r--core/java/com/android/internal/util/UserIcons.java4
-rw-r--r--core/java/com/android/internal/view/ActionModeWrapper.java211
-rw-r--r--core/java/com/android/internal/widget/AccessibleDateAnimator.java56
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java596
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java6
-rw-r--r--core/java/com/android/internal/widget/ViewPager.java34
166 files changed, 8511 insertions, 6399 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7fcbe35..b5817df 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3726,6 +3726,95 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * #checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * showContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see #checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+ startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - no nothing */
+ }
+
+ /**
* Same as calling {@link #startActivityForResult(Intent, int, Bundle)}
* with no options.
*
@@ -6269,11 +6358,19 @@ public class Activity extends ContextThemeWrapper
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
if (who == null) {
- onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResult(requestCode, data);
+ } else {
+ onActivityResult(requestCode, resultCode, data);
+ }
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
- frag.onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
+ } else {
+ frag.onActivityResult(requestCode, resultCode, data);
+ }
}
}
}
@@ -6343,4 +6440,26 @@ public class Activity extends ContextThemeWrapper
*/
public void onTranslucentConversionComplete(boolean drawComplete);
}
+
+ private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data,
+ Fragment fragement) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ fragement.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private static boolean isRequestPermissionResult(Intent intent) {
+ return intent != null
+ && PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 29b024ac..d143f8b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2493,7 +2493,8 @@ public class ActivityManager {
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
- if (uid == 0 || uid == Process.SYSTEM_UID) {
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7b8ec74..4880db1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -74,6 +74,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.security.NetworkSecurityPolicy;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -4480,6 +4481,9 @@ public final class ActivityThread {
StrictMode.enableDeathOnNetwork();
}
+ NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(
+ (data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0);
+
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 2cb27b0..eafcdb2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -350,10 +350,15 @@ public class ActivityView extends ViewGroup {
if (activityView != null) {
final ActivityViewCallback callback = activityView.mActivityViewCallback;
if (callback != null) {
+ final WeakReference<ActivityViewCallback> callbackRef =
+ new WeakReference<>(callback);
activityView.post(new Runnable() {
@Override
public void run() {
- callback.onAllActivitiesComplete(activityView);
+ ActivityViewCallback callback = callbackRef.get();
+ if (callback != null) {
+ callback.onAllActivitiesComplete(activityView);
+ }
}
});
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9f81670..6d74905 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -81,7 +81,6 @@ import java.util.List;
/*package*/
final class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
- private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
// Default flags to use with PackageManager when no flags are given.
@@ -186,8 +185,8 @@ final class ApplicationPackageManager extends PackageManager {
public int[] getPackageGids(String packageName)
throws NameNotFoundException {
try {
- int[] gids = mPM.getPackageGids(packageName);
- if (gids == null || gids.length > 0) {
+ int[] gids = mPM.getPackageGids(packageName, mContext.getUserId());
+ if (gids != null) {
return gids;
}
} catch (RemoteException e) {
@@ -398,7 +397,7 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public int checkPermission(String permName, String pkgName) {
try {
- return mPM.checkPermission(permName, pkgName);
+ return mPM.checkPermission(permName, pkgName, mContext.getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -432,18 +431,18 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public void grantPermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.grantPermission(packageName, permissionName);
+ mPM.grantPermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
@Override
- public void revokePermission(String packageName, String permissionName) {
+ public void revokePermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.revokePermission(packageName, permissionName);
+ mPM.revokePermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 25153fc..c435ccb 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,6 +17,7 @@
package android.app;
import android.content.ComponentName;
+import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
@@ -53,7 +54,7 @@ final public class AssistStructure implements Parcelable {
final ComponentName mActivityComponent;
- final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>();
+ final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl();
Bundle mTmpExtras = new Bundle();
@@ -178,7 +179,91 @@ final public class AssistStructure implements Parcelable {
}
}
- final static class ViewNodeImpl {
+ /**
+ * Describes a window in the assist data.
+ */
+ static public class WindowNode {
+ final int mX;
+ final int mY;
+ final int mWidth;
+ final int mHeight;
+ final CharSequence mTitle;
+ final ViewNode mRoot;
+
+ WindowNode(AssistStructure assist, ViewRootImpl root) {
+ View view = root.getView();
+ Rect rect = new Rect();
+ view.getBoundsOnScreen(rect);
+ mX = rect.left - view.getLeft();
+ mY = rect.top - view.getTop();
+ mWidth = rect.width();
+ mHeight = rect.height();
+ mTitle = root.getTitle();
+ mRoot = new ViewNode(assist, view);
+ }
+
+ WindowNode(Parcel in, PooledStringReader preader) {
+ mX = in.readInt();
+ mY = in.readInt();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mRoot = new ViewNode(in, preader);
+ }
+
+ void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+ out.writeInt(mX);
+ out.writeInt(mY);
+ out.writeInt(mWidth);
+ out.writeInt(mHeight);
+ TextUtils.writeToParcel(mTitle, out, 0);
+ mRoot.writeToParcel(out, pwriter);
+ }
+
+ public int getLeft() {
+ return mX;
+ }
+
+ public int getTop() {
+ return mY;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public ViewNode getRootViewNode() {
+ return mRoot;
+ }
+ }
+
+ /**
+ * Describes a single view in the assist data.
+ */
+ static public class ViewNode {
+ /**
+ * Magic value for text color that has not been defined, which is very unlikely
+ * to be confused with a real text color.
+ */
+ public static final int TEXT_COLOR_UNDEFINED = 1;
+
+ public static final int TEXT_STYLE_BOLD = 1<<0;
+ public static final int TEXT_STYLE_ITALIC = 1<<1;
+ public static final int TEXT_STYLE_UNDERLINE = 1<<2;
+ public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+
+ final int mId;
+ final String mIdPackage;
+ final String mIdType;
+ final String mIdEntry;
final int mX;
final int mY;
final int mScrollX;
@@ -201,17 +286,34 @@ final public class AssistStructure implements Parcelable {
final int mFlags;
final String mClassName;
- final String mContentDescription;
+ final CharSequence mContentDescription;
final ViewNodeTextImpl mText;
final Bundle mExtras;
- final ViewNodeImpl[] mChildren;
-
- ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top,
- CharSequence contentDescription) {
- mX = left;
- mY = top;
+ final ViewNode[] mChildren;
+
+ ViewNode(AssistStructure assistStructure, View view) {
+ mId = view.getId();
+ if (mId > 0 && (mId&0xff000000) != 0 && (mId&0x00ff0000) != 0
+ && (mId&0x0000ffff) != 0) {
+ String pkg, type, entry;
+ try {
+ Resources res = view.getResources();
+ entry = res.getResourceEntryName(mId);
+ type = res.getResourceTypeName(mId);
+ pkg = res.getResourcePackageName(mId);
+ } catch (Resources.NotFoundException e) {
+ entry = type = pkg = null;
+ }
+ mIdPackage = pkg;
+ mIdType = type;
+ mIdEntry = entry;
+ } else {
+ mIdPackage = mIdType = mIdEntry = null;
+ }
+ mX = view.getLeft();
+ mY = view.getTop();
mScrollX = view.getScrollX();
mScrollY = view.getScrollY();
mWidth = view.getWidth();
@@ -249,7 +351,7 @@ final public class AssistStructure implements Parcelable {
}
mFlags = flags;
mClassName = view.getAccessibilityClassName().toString();
- mContentDescription = contentDescription != null ? contentDescription.toString() : null;
+ mContentDescription = view.getContentDescription();
final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl;
final Bundle extras = assistStructure.mTmpExtras;
view.onProvideAssistStructure(viewData, extras);
@@ -269,9 +371,9 @@ final public class AssistStructure implements Parcelable {
ViewGroup vg = (ViewGroup)view;
final int NCHILDREN = vg.getChildCount();
if (NCHILDREN > 0) {
- mChildren = new ViewNodeImpl[NCHILDREN];
+ mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i));
+ mChildren[i] = new ViewNode(assistStructure, vg.getChildAt(i));
}
} else {
mChildren = null;
@@ -281,11 +383,19 @@ final public class AssistStructure implements Parcelable {
}
}
- ViewNodeImpl(AssistStructure assistStructure, View view) {
- this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription());
- }
-
- ViewNodeImpl(Parcel in, PooledStringReader preader) {
+ ViewNode(Parcel in, PooledStringReader preader) {
+ mId = in.readInt();
+ if (mId != 0) {
+ mIdEntry = preader.readString();
+ if (mIdEntry != null) {
+ mIdType = preader.readString();
+ mIdPackage = preader.readString();
+ } else {
+ mIdPackage = mIdType = null;
+ }
+ } else {
+ mIdPackage = mIdType = mIdEntry = null;
+ }
mX = in.readInt();
mY = in.readInt();
mScrollX = in.readInt();
@@ -294,7 +404,7 @@ final public class AssistStructure implements Parcelable {
mHeight = in.readInt();
mFlags = in.readInt();
mClassName = preader.readString();
- mContentDescription = in.readString();
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
mText = new ViewNodeTextImpl(in);
} else {
@@ -303,9 +413,9 @@ final public class AssistStructure implements Parcelable {
mExtras = in.readBundle();
final int NCHILDREN = in.readInt();
if (NCHILDREN > 0) {
- mChildren = new ViewNodeImpl[NCHILDREN];
+ mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNodeImpl(in, preader);
+ mChildren[i] = new ViewNode(in, preader);
}
} else {
mChildren = null;
@@ -313,6 +423,14 @@ final public class AssistStructure implements Parcelable {
}
void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+ out.writeInt(mId);
+ if (mId != 0) {
+ pwriter.writeString(mIdEntry);
+ if (mIdEntry != null) {
+ pwriter.writeString(mIdType);
+ pwriter.writeString(mIdPackage);
+ }
+ }
out.writeInt(mX);
out.writeInt(mY);
out.writeInt(mScrollX);
@@ -321,7 +439,7 @@ final public class AssistStructure implements Parcelable {
out.writeInt(mHeight);
out.writeInt(mFlags);
pwriter.writeString(mClassName);
- out.writeString(mContentDescription);
+ TextUtils.writeToParcel(mContentDescription, out, 0);
if (mText != null) {
out.writeInt(1);
mText.writeToParcel(out);
@@ -339,146 +457,141 @@ final public class AssistStructure implements Parcelable {
out.writeInt(0);
}
}
- }
- /**
- * Provides access to information about a single view in the assist data.
- */
- static public class ViewNode {
- /**
- * Magic value for text color that has not been defined, which is very unlikely
- * to be confused with a real text color.
- */
- public static final int TEXT_COLOR_UNDEFINED = 1;
+ public int getId() {
+ return mId;
+ }
- public static final int TEXT_STYLE_BOLD = 1<<0;
- public static final int TEXT_STYLE_ITALIC = 1<<1;
- public static final int TEXT_STYLE_UNDERLINE = 1<<2;
- public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+ public String getIdPackage() {
+ return mIdPackage;
+ }
- ViewNodeImpl mImpl;
+ public String getIdType() {
+ return mIdType;
+ }
- public ViewNode() {
+ public String getIdEntry() {
+ return mIdEntry;
}
public int getLeft() {
- return mImpl.mX;
+ return mX;
}
public int getTop() {
- return mImpl.mY;
+ return mY;
}
public int getScrollX() {
- return mImpl.mScrollX;
+ return mScrollX;
}
public int getScrollY() {
- return mImpl.mScrollY;
+ return mScrollY;
}
public int getWidth() {
- return mImpl.mWidth;
+ return mWidth;
}
public int getHeight() {
- return mImpl.mHeight;
+ return mHeight;
}
public int getVisibility() {
- return mImpl.mFlags&ViewNodeImpl.FLAGS_VISIBILITY_MASK;
+ return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
public boolean isEnabled() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_DISABLED) == 0;
+ return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
public boolean isClickable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CLICKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
}
public boolean isFocusable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
}
public boolean isFocused() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSED) != 0;
+ return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
}
public boolean isAccessibilityFocused() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
+ return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
}
public boolean isCheckable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
}
public boolean isChecked() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKED) != 0;
+ return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
}
public boolean isSelected() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_SELECTED) != 0;
+ return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
}
public boolean isActivated() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACTIVATED) != 0;
+ return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
}
public boolean isLongClickable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_LONG_CLICKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
}
public String getClassName() {
- return mImpl.mClassName;
+ return mClassName;
}
- public String getContentDescription() {
- return mImpl.mContentDescription;
+ public CharSequence getContentDescription() {
+ return mContentDescription;
}
public CharSequence getText() {
- return mImpl.mText != null ? mImpl.mText.mText : null;
+ return mText != null ? mText.mText : null;
}
public int getTextSelectionStart() {
- return mImpl.mText != null ? mImpl.mText.mTextSelectionStart : -1;
+ return mText != null ? mText.mTextSelectionStart : -1;
}
public int getTextSelectionEnd() {
- return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1;
+ return mText != null ? mText.mTextSelectionEnd : -1;
}
public int getTextColor() {
- return mImpl.mText != null ? mImpl.mText.mTextColor : TEXT_COLOR_UNDEFINED;
+ return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
}
public int getTextBackgroundColor() {
- return mImpl.mText != null ? mImpl.mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
+ return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
}
public float getTextSize() {
- return mImpl.mText != null ? mImpl.mText.mTextSize : 0;
+ return mText != null ? mText.mTextSize : 0;
}
public int getTextStyle() {
- return mImpl.mText != null ? mImpl.mText.mTextStyle : 0;
+ return mText != null ? mText.mTextStyle : 0;
}
public String getHint() {
- return mImpl.mText != null ? mImpl.mText.mHint : null;
+ return mText != null ? mText.mHint : null;
}
public Bundle getExtras() {
- return mImpl.mExtras;
+ return mExtras;
}
public int getChildCount() {
- return mImpl.mChildren != null ? mImpl.mChildren.length : 0;
+ return mChildren != null ? mChildren.length : 0;
}
- public void getChildAt(int index, ViewNode outNode) {
- outNode.mImpl = mImpl.mChildren[index];
+ public ViewNode getChildAt(int index) {
+ return mChildren[index];
}
}
@@ -488,12 +601,7 @@ final public class AssistStructure implements Parcelable {
activity.getActivityToken());
for (int i=0; i<views.size(); i++) {
ViewRootImpl root = views.get(i);
- View view = root.getView();
- Rect rect = new Rect();
- view.getBoundsOnScreen(rect);
- CharSequence title = root.getTitle();
- mRootViews.add(new ViewNodeImpl(this, view, rect.left, rect.top,
- title != null ? title : view.getContentDescription()));
+ mWindowNodes.add(new WindowNode(this, root));
}
}
@@ -502,7 +610,7 @@ final public class AssistStructure implements Parcelable {
mActivityComponent = ComponentName.readFromParcel(in);
final int N = in.readInt();
for (int i=0; i<N; i++) {
- mRootViews.add(new ViewNodeImpl(in, preader));
+ mWindowNodes.add(new WindowNode(in, preader));
}
//dump();
}
@@ -510,24 +618,37 @@ final public class AssistStructure implements Parcelable {
/** @hide */
public void dump() {
Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
- ViewNode node = new ViewNode();
- final int N = getWindowCount();
+ final int N = getWindowNodeCount();
for (int i=0; i<N; i++) {
- Log.i(TAG, "Window #" + i + ":");
- getWindowAt(i, node);
- dump(" ", node);
+ WindowNode node = getWindowNodeAt(i);
+ Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
+ + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
+ dump(" ", node.getRootViewNode());
}
}
void dump(String prefix, ViewNode node) {
Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
+ " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
+ int id = node.getId();
+ if (id != 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id));
+ String entry = node.getIdEntry();
+ if (entry != null) {
+ String type = node.getIdType();
+ String pkg = node.getIdPackage();
+ sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
+ sb.append("/"); sb.append(entry);
+ }
+ Log.i(TAG, sb.toString());
+ }
int scrollX = node.getScrollX();
int scrollY = node.getScrollY();
if (scrollX != 0 || scrollY != 0) {
Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY);
}
- String contentDescription = node.getContentDescription();
+ CharSequence contentDescription = node.getContentDescription();
if (contentDescription != null) {
Log.i(TAG, prefix + " Content description: " + contentDescription);
}
@@ -552,9 +673,8 @@ final public class AssistStructure implements Parcelable {
if (NCHILDREN > 0) {
Log.i(TAG, prefix + " Children:");
String cprefix = prefix + " ";
- ViewNode cnode = new ViewNode();
for (int i=0; i<NCHILDREN; i++) {
- node.getChildAt(i, cnode);
+ ViewNode cnode = node.getChildAt(i);
dump(cprefix, cnode);
}
}
@@ -575,17 +695,16 @@ final public class AssistStructure implements Parcelable {
/**
* Return the number of window contents that have been collected in this assist data.
*/
- public int getWindowCount() {
- return mRootViews.size();
+ public int getWindowNodeCount() {
+ return mWindowNodes.size();
}
/**
- * Return the root view for one of the windows in the assist data.
- * @param index Which window to retrieve, may be 0 to {@link #getWindowCount()}-1.
- * @param outNode Node in which to place the window's root view.
+ * Return one of the windows in the assist data.
+ * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
*/
- public void getWindowAt(int index, ViewNode outNode) {
- outNode.mImpl = mRootViews.get(index);
+ public WindowNode getWindowNodeAt(int index) {
+ return mWindowNodes.get(index);
}
public int describeContents() {
@@ -596,10 +715,10 @@ final public class AssistStructure implements Parcelable {
int start = out.dataPosition();
PooledStringWriter pwriter = new PooledStringWriter(out);
ComponentName.writeToParcel(mActivityComponent, out);
- final int N = mRootViews.size();
+ final int N = mWindowNodes.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
- mRootViews.get(i).writeToParcel(out, pwriter);
+ mWindowNodes.get(i).writeToParcel(out, pwriter);
}
pwriter.finish();
Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes");
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index eb27830..4ccd69f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1323,6 +1323,15 @@ class ContextImpl extends Context {
Binder.getCallingUid());
}
+ @Override
+ public int checkSelfPermission(String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ return checkPermission(permission, Process.myPid(), Process.myUid());
+ }
+
private void enforce(
String permission, int resultOfCheck,
boolean selfToo, int uid, String message) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index bdcc312..4fdae7f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,6 +17,7 @@
package android.app;
import android.animation.Animator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.ComponentCallbacks2;
@@ -1092,13 +1093,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- if (options != null) {
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- } else {
- // Note we want to go through this call for compatibility with
- // applications that may have overridden the method.
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- }
+ mActivity.startActivityFromFragment(this, intent, requestCode, options);
}
/**
@@ -1119,6 +1114,98 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * android.content.Context#checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * doShowContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see android.content.Context#checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ if (mActivity == null) {
+ throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+ }
+ Intent intent = mActivity.getPackageManager().buildRequestPermissionsIntent(permissions);
+ mActivity.startActivityFromFragment(this, intent, requestCode, null);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - do nothing */
+ }
+
+ /**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5d864df..33262b3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,6 +48,9 @@ interface INotificationManager
void setPackagePriority(String pkg, int uid, int priority);
int getPackagePriority(String pkg, int uid);
+ void setPackagePeekable(String pkg, int uid, boolean peekable);
+ boolean getPackagePeekable(String pkg, int uid);
+
void setPackageVisibilityOverride(String pkg, int uid, int visibility);
int getPackageVisibilityOverride(String pkg, int uid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 85a6aff..b31ce04 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1840,6 +1840,26 @@ public class Notification implements Parcelable
}
/**
+ * {@hide}
+ */
+ public static String priorityToString(@Priority int pri) {
+ switch (pri) {
+ case PRIORITY_MIN:
+ return "MIN";
+ case PRIORITY_LOW:
+ return "LOW";
+ case PRIORITY_DEFAULT:
+ return "DEFAULT";
+ case PRIORITY_HIGH:
+ return "HIGH";
+ case PRIORITY_MAX:
+ return "MAX";
+ default:
+ return "UNKNOWN(" + String.valueOf(pri) + ")";
+ }
+ }
+
+ /**
* @hide
*/
public boolean isValid() {
@@ -2870,7 +2890,7 @@ public class Notification implements Parcelable
contentView.setProgressBar(
R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
contentView.setProgressBackgroundTintList(
- R.id.progress, ColorStateList.valueOf(mContext.getResources().getColor(
+ R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
if (mColor != COLOR_DEFAULT) {
ColorStateList colorStateList = ColorStateList.valueOf(mColor);
@@ -3044,7 +3064,7 @@ public class Notification implements Parcelable
private void processLegacyAction(Action action, RemoteViews button) {
if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
- mContext.getResources().getColor(R.color.notification_action_color_filter),
+ mContext.getColor(R.color.notification_action_color_filter),
PorterDuff.Mode.MULTIPLY);
}
}
@@ -3142,7 +3162,7 @@ public class Notification implements Parcelable
private int resolveColor() {
if (mColor == COLOR_DEFAULT) {
- return mContext.getResources().getColor(R.color.notification_icon_bg_color);
+ return mContext.getColor(R.color.notification_icon_bg_color);
}
return mColor;
}
@@ -4321,9 +4341,9 @@ public class Notification implements Parcelable
* Applies the special text colors for media notifications to all text views.
*/
private void styleText(RemoteViews contentView) {
- int primaryColor = mBuilder.mContext.getResources().getColor(
+ int primaryColor = mBuilder.mContext.getColor(
R.color.notification_media_primary_color);
- int secondaryColor = mBuilder.mContext.getResources().getColor(
+ int secondaryColor = mBuilder.mContext.getColor(
R.color.notification_media_secondary_color);
contentView.setTextColor(R.id.title, primaryColor);
if (mBuilder.showsTimeOrChronometer()) {
@@ -5468,6 +5488,470 @@ public class Notification implements Parcelable
}
/**
+ * <p>
+ * Helper class to add content info extensions to notifications. To create a notification with
+ * content info extensions:
+ * <ol>
+ * <li>Create an {@link Notification.Builder}, setting any desired properties.
+ * <li>Create a {@link ContentInfoExtender}.
+ * <li>Set content info specific properties using the {@code add} and {@code set} methods of
+ * {@link ContentInfoExtender}.
+ * <li>Call {@link Notification.Builder#extend(Notification.Extender)} to apply the extensions
+ * to a notification.
+ * </ol>
+ *
+ * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new ContentInfoExtender() * .set*(...)) * .build(); * </pre>
+ * <p>
+ * Content info extensions can be accessed on an existing notification by using the
+ * {@code ContentInfoExtender(Notification)} constructor, and then using the {@code get} methods
+ * to access values.
+ */
+ public static final class ContentInfoExtender implements Extender {
+ private static final String TAG = "ContentInfoExtender";
+
+ // Key for the Content info extensions bundle in the main Notification extras bundle
+ private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
+
+ // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
+
+ private static final String KEY_CONTENT_TYPE = "android.contentType";
+
+ private static final String KEY_CONTENT_GENRES = "android.contentGenre";
+
+ private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
+
+ private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
+
+ private static final String KEY_CONTENT_STATUS = "android.contentStatus";
+
+ private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
+
+ private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
+
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a video clip.
+ */
+ public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a movie.
+ */
+ public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a trailer.
+ */
+ public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is serial. It can refer to an entire show, a single season or
+ * series, or a single episode.
+ */
+ public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a song or album.
+ */
+ public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a radio station.
+ */
+ public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a podcast.
+ */
+ public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a news item.
+ */
+ public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is sports.
+ */
+ public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is an application.
+ */
+ public static final String CONTENT_TYPE_APP = "android.contentType.app";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a game.
+ */
+ public static final String CONTENT_TYPE_GAME = "android.contentType.game";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a book.
+ */
+ public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a comic book.
+ */
+ public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a magazine.
+ */
+ public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+
+ /**
+ * Value to be used with {@link #setContentTypes} to indicate that the content referred by
+ * the notification item is a website.
+ */
+ public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is free to consume.
+ */
+ public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available as a rental, and the price value provided
+ * is the rental price for the item.
+ */
+ public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available for purchase, and the price value provided
+ * is the purchase price for the item.
+ */
+ public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available as part of a subscription based service,
+ * and the price value provided is the subscription price for the service.
+ */
+ public static final String CONTENT_PRICING_SUBSCRIPTION =
+ "android.contentPrice.subscription";
+
+ /**
+ * Value to be used with {@link #setStatus} to indicate that the content referred by the
+ * notification is available and ready to be consumed immediately.
+ */
+ public static final int CONTENT_STATUS_READY = 0;
+
+ /**
+ * Value to be used with {@link #setStatus} to indicate that the content referred by the
+ * notification is pending, waiting on either a download or purchase operation to complete
+ * before it can be consumed.
+ */
+ public static final int CONTENT_STATUS_PENDING = 1;
+
+ /**
+ * Value to be used with {@link #setStatus} to indicate that the content referred by the
+ * notification is available, but needs to be first purchased, rented, subscribed or
+ * downloaded before it can be consumed.
+ */
+ public static final int CONTENT_STATUS_AVAILABLE = 2;
+
+ /**
+ * Value to be used with {@link #setStatus} to indicate that the content referred by the
+ * notification is not available. This could be content not available in a certain region or
+ * incompatible with the device in use.
+ */
+ public static final int CONTENT_STATUS_UNAVAILABLE = 3;
+
+ /**
+ * Value to be used with {@link #setMaturityRating} to indicate that the content referred by
+ * the notification is suitable for all audiences.
+ */
+ public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+
+ /**
+ * Value to be used with {@link #setMaturityRating} to indicate that the content
+ * referred by the notification is suitable for audiences of low maturity and above.
+ */
+ public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+
+ /**
+ * Value to be used with {@link #setMaturityRating} to indicate that the content
+ * referred by the notification is suitable for audiences of medium maturity and above.
+ */
+ public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+
+ /**
+ * Value to be used with {@link #setMaturityRating} to indicate that the content
+ * referred by the notification is suitable for audiences of high maturity and above.
+ */
+ public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+
+ private String[] mTypes;
+ private String[] mGenres;
+ private String mPricingType;
+ private String mPricingValue;
+ private int mContentStatus = -1;
+ private String mMaturityRating;
+ private long mRunLength = -1;
+
+ /**
+ * Create a {@link ContentInfoExtender} with default options.
+ */
+ public ContentInfoExtender() {
+ }
+
+ /**
+ * Create a {@link ContentInfoExtender} from the ContentInfoExtender options of an existing
+ * Notification.
+ *
+ * @param notif The notification from which to copy options.
+ */
+ public ContentInfoExtender(Notification notif) {
+ Bundle contentBundle = notif.extras == null ?
+ null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
+ if (contentBundle != null) {
+ mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
+ mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
+ mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
+ mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
+ mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
+ mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
+ mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
+ }
+ }
+
+ /**
+ * Apply content extensions to a notification that is being built. This is typically called
+ * by the {@link Notification.Builder#extend(Notification.Extender)} method of
+ * {@link Notification.Builder}.
+ */
+ @Override
+ public Notification.Builder extend(Notification.Builder builder) {
+ Bundle contentBundle = new Bundle();
+
+ if (mTypes != null) {
+ contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
+ }
+ if (mGenres != null) {
+ contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
+ }
+ if (mPricingType != null) {
+ contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
+ }
+ if (mPricingValue != null) {
+ contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
+ }
+ if (mContentStatus != -1) {
+ contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
+ }
+ if (mMaturityRating != null) {
+ contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
+ }
+ if (mRunLength > 0) {
+ contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
+ }
+
+ builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
+ return builder;
+ }
+
+ /**
+ * Sets the content types associated with the notification content. The first tag entry will
+ * be considered the primary type for the content and will be used for ranking purposes.
+ * Other secondary type tags may be provided, if applicable, and may be used for filtering
+ * purposes.
+ *
+ * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
+ * constants) that describe the content referred to by a notification.
+ */
+ public ContentInfoExtender setContentTypes(String[] types) {
+ mTypes = types;
+ return this;
+ }
+
+ /**
+ * Returns an array containing the content types that describe the content associated with
+ * the notification. The first tag entry is considered the primary type for the content, and
+ * is used for content ranking purposes.
+ *
+ * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
+ * that describe the content associated with the notification.
+ * @see ContentInfoExtender#setContentTypes
+ */
+ public String[] getContentTypes() {
+ return mTypes;
+ }
+
+ /**
+ * Returns the primary content type tag for the content associated with the notification.
+ *
+ * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating
+ * the primary type for the content associated with the notification.
+ * @see ContentInfoExtender#setContentTypes
+ */
+ public String getPrimaryContentType() {
+ if (mTypes == null || mTypes.length == 0) {
+ return null;
+ }
+ return mTypes[0];
+ }
+
+ /**
+ * Sets the content genres associated with the notification content. These genres may be
+ * used for content ranking. Genres are open ended String tags.
+ * <p>
+ * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
+ *
+ * @param genres Array of genre string tags that describe the content referred to by a
+ * notification.
+ */
+ public ContentInfoExtender setGenres(String[] genres) {
+ mGenres = genres;
+ return this;
+ }
+
+ /**
+ * Returns an array containing the content genres that describe the content associated with
+ * the notification.
+ *
+ * @return An array of genre tags that describe the content associated with the
+ * notification.
+ * @see ContentInfoExtender#setGenres
+ */
+ public String[] getGenres() {
+ return mGenres;
+ }
+
+ /**
+ * Sets the pricing and availability information for the content associated with the
+ * notification. The provided information will indicate the access model for the content
+ * (free, rental, purchase or subscription) and the price value (if not free).
+ *
+ * @param priceType Pricing type for this content. Must be one of the predefined pricing
+ * type tags (see the <code>CONTENT_PRICING_*</code> constants).
+ * @param priceValue A string containing a representation of the content price in the
+ * current locale and currency.
+ * @return This object for method chaining.
+ */
+ public ContentInfoExtender setPricingInformation(String priceType, String priceValue) {
+ mPricingType = priceType;
+ mPricingValue = priceValue;
+ return this;
+ }
+
+ /**
+ * Gets the pricing type for the content associated with the notification.
+ *
+ * @return A predefined tag indicating the pricing type for the content (see the <code>
+ * CONTENT_PRICING_*</code> constants).
+ * @see ContentInfoExtender#setPricingInformation
+ */
+ public String getPricingType() {
+ return mPricingType;
+ }
+
+ /**
+ * Gets the price value (when applicable) for the content associated with a notification.
+ * The value will be provided as a String containing the price in the appropriate currency
+ * for the current locale.
+ *
+ * @return A string containing a representation of the content price in the current locale
+ * and currency.
+ * @see ContentInfoExtender#setPricingInformation
+ */
+ public String getPricingValue() {
+ if (mPricingType == null || CONTENT_PRICING_FREE.equals(mPricingType)) {
+ return null;
+ }
+ return mPricingValue;
+ }
+
+ /**
+ * Sets the availability status for the content associated with the notification. This
+ * status indicates whether the referred content is ready to be consumed on the device, or
+ * if the user must first purchase, rent, subscribe to, or download the content.
+ *
+ * @param contentStatus The status value for this content. Must be one of the predefined
+ * content status values (see the <code>CONTENT_STATUS_*</code> constants).
+ */
+ public ContentInfoExtender setStatus(int contentStatus) {
+ mContentStatus = contentStatus;
+ return this;
+ }
+
+ /**
+ * Returns status value for the content associated with the notification. This status
+ * indicates whether the referred content is ready to be consumed on the device, or if the
+ * user must first purchase, rent, subscribe to, or download the content.
+ *
+ * @return The status value for this content, or -1 is a valid status has not been specified
+ * (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
+ * @see ContentInfoExtender#setStatus
+ */
+ public int getStatus() {
+ return mContentStatus;
+ }
+
+ /**
+ * Sets the maturity level rating for the content associated with the notification.
+ *
+ * @param maturityRating A tag indicating the maturity level rating for the content. This
+ * tag must be one of the predefined maturity rating tags (see the <code>
+ * CONTENT_MATURITY_*</code> constants).
+ */
+ public ContentInfoExtender setMaturityRating(String maturityRating) {
+ mMaturityRating = maturityRating;
+ return this;
+ }
+
+ /**
+ * Returns the maturity level rating for the content associated with the notification.
+ *
+ * @return returns a predefined tag indicating the maturity level rating for the content
+ * (see the <code> CONTENT_MATURITY_*</code> constants).
+ * @see ContentInfoExtender#setMaturityRating
+ */
+ public String getMaturityRating() {
+ return mMaturityRating;
+ }
+
+ /**
+ * Sets the running time (when applicable) for the content associated with the notification.
+ *
+ * @param length The runing time, in seconds, of the content associated with the
+ * notification.
+ */
+ public ContentInfoExtender setRunningTime(long length) {
+ mRunLength = length;
+ return this;
+ }
+
+ /**
+ * Returns the running time for the content associated with the notification.
+ *
+ * @return The running time, in seconds, of the content associated with the notification.
+ * @see ContentInfoExtender#setRunningTime
+ */
+ public long getRunningTime() {
+ return mRunLength;
+ }
+ }
+
+ /**
* Get an array of Notification objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fd7bae7..59fe490 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -27,6 +27,7 @@ import android.app.job.IJobScheduler;
import android.app.job.JobScheduler;
import android.app.trust.TrustManager;
import android.app.usage.IUsageStatsManager;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothManager;
@@ -639,6 +640,13 @@ final class SystemServiceRegistry {
return new UsageStatsManager(ctx.getOuterContext(), service);
}});
+ registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class,
+ new CachedServiceFetcher<NetworkStatsManager>() {
+ @Override
+ public NetworkStatsManager createService(ContextImpl ctx) {
+ return new NetworkStatsManager(ctx.getOuterContext());
+ }});
+
registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
new StaticServiceFetcher<JobScheduler>() {
@Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 81bcb39..9ba6a8e 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -227,7 +227,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
@Override
- public void executeShellCommand(String command, ParcelFileDescriptor sink)
+ public void executeShellCommand(final String command, final ParcelFileDescriptor sink)
throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
@@ -235,30 +235,35 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
throwIfNotConnectedLocked();
}
- InputStream in = null;
- OutputStream out = null;
-
- try {
- java.lang.Process process = Runtime.getRuntime().exec(command);
-
- in = process.getInputStream();
- out = new FileOutputStream(sink.getFileDescriptor());
-
- final byte[] buffer = new byte[8192];
- while (true) {
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
+ Thread streamReader = new Thread() {
+ public void run() {
+ InputStream in = null;
+ OutputStream out = null;
+
+ try {
+ java.lang.Process process = Runtime.getRuntime().exec(command);
+
+ in = process.getInputStream();
+ out = new FileOutputStream(sink.getFileDescriptor());
+
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException("Error running shell command", ioe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
}
- out.write(buffer, 0, readByteCount);
- }
- } catch (IOException ioe) {
- throw new RuntimeException("Error running shell command", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(sink);
- }
+ };
+ };
+ streamReader.start();
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a659acb..cf6619f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,7 +20,6 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.app.Activity;
-import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -176,7 +175,8 @@ public class DevicePolicyManager {
*
* <p>This component is set as device owner and active admin when device owner provisioning is
* started by an NFC message containing an NFC record with MIME type
- * {@link #MIME_TYPE_PROVISIONING_NFC}.
+ * {@link #MIME_TYPE_PROVISIONING_NFC_V2}. For the NFC record, the component name should be
+ * flattened to a string, via {@link ComponentName#flattenToShortString()}.
*
* @see DeviceAdminReceiver
*/
@@ -341,6 +341,18 @@ public class DevicePolicyManager {
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
/**
+ * An int extra holding a minimum required version code for the device admin package. If the
+ * device admin is already installed on the device, it will only be re-downloaded from
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the
+ * installed package is less than this version code.
+ *
+ * <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_ADMIN_MINIMUM_VERSION_CODE
+ = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
+
+ /**
* A String extra holding a http cookie header which should be used in the http request to the
* url specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
*
@@ -351,10 +363,10 @@ public class DevicePolicyManager {
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
/**
- * A String extra holding the 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.
+ * 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.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
@@ -380,21 +392,23 @@ public class DevicePolicyManager {
* A boolean extra indicating whether device encryption is required as part of Device Owner
* provisioning.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * <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_SKIP_ENCRYPTION =
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
/**
- * On devices managed by a device owner app, a String representation of a Component name extra
- * indicating the component of the application that is temporarily granted device owner
- * privileges during device initialization and profile owner privileges during secondary user
- * initialization.
+ * On devices managed by a device owner app, a {@link ComponentName} extra indicating the
+ * component of the application that is temporarily granted device owner privileges during
+ * device initialization and profile owner privileges during secondary user initialization.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an NFC bump.
- * @see ComponentName#unflattenFromString()
+ * <p>
+ * It can also be used in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts
+ * device owner provisioning via an NFC bump. For the NFC record, it should be flattened to a
+ * string first.
+ *
+ * @see ComponentName#flattenToShortString()
*/
public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
@@ -404,53 +418,105 @@ public class DevicePolicyManager {
* initializer package. When not provided it is assumed that the device initializer package is
* already installed.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * <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_PACKAGE_DOWNLOAD_LOCATION
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
/**
+ * An int extra holding a minimum required version code for the device initializer package.
+ * If the initializer is already installed on the device, it will only be re-downloaded from
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of
+ * the installed package is less than this version code.
+ *
+ * <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_MINIMUM_VERSION_CODE
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
+
+ /**
* A String extra holding a http cookie header which should be used in the http request to the
* url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * <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_PACKAGE_DOWNLOAD_COOKIE_HEADER
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
/**
- * A String extra holding the SHA-1 checksum of the file at download location specified in
+ * 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.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * <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_PACKAGE_CHECKSUM
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
/**
- * This MIME type is used for starting the Device Owner provisioning.
+ * A String extra holding the MAC address of the Bluetooth device to connect to with status
+ * updates during provisioning.
*
- * <p>During device owner provisioning a device admin app is set as the owner of the device.
- * A device owner has full control over the device. The device owner can not be modified by the
- * user and the only way of resetting the device is if the device owner app calls a factory
- * reset.
+ * <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_BT_MAC_ADDRESS
+ = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+
+ /**
+ * A String extra holding the Bluetooth service UUID on the device to connect to with status
+ * updates during provisioning.
*
- * <p> A typical use case would be a device that is owned by a company, but used by either an
- * employee or client.
+ * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
+ *
+ * <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_BT_UUID
+ = "android.app.extra.PROVISIONING_BT_UUID";
+
+ /**
+ * A String extra holding a unique identifier used to identify the device connecting over
+ * Bluetooth. This identifier will be part of every status message sent to the remote device.
+ *
+ * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
+ *
+ * <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_BT_DEVICE_ID
+ = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+
+ /**
+ * A Boolean extra that that will cause a provisioned device to temporarily proxy network
+ * traffic over Bluetooth. When a Wi-Fi network is available, the network proxy will stop.
+ *
+ * <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_BT_USE_PROXY
+ = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+ /**
+ * This MIME type is used for starting the Device Owner provisioning that does not require
+ * provisioning features introduced in Android API level
+ * {@link android.os.Build.VERSION_CODES#MNC} or later levels.
*
- * <p> The NFC message should be send to an unprovisioned device.
+ * <p>For more information about the provisioning process see
+ * {@link #MIME_TYPE_PROVISIONING_NFC_V2}.
*
* <p>The NFC record must contain a serialized {@link java.util.Properties} object which
* contains the following properties:
* <ul>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
@@ -461,17 +527,56 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li></ul>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li></ul>
*
* <p>
- * In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, it should also contain
- * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}.
- * As of {@link android.os.Build.VERSION_CODES#MNC}, it should contain
- * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, (although
- * specifying only {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported).
- * This componentName must have been converted to a String via
- * {@link android.content.ComponentName#flattenToString()}
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, the properties should contain
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead of
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}, (although specifying only
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported).
+ *
+ * @see #MIME_TYPE_PROVISIONING_NFC_V2
+ *
+ */
+ public static final String MIME_TYPE_PROVISIONING_NFC
+ = "application/com.android.managedprovisioning";
+
+
+ /**
+ * This MIME type is used for starting the Device Owner provisioning that requires
+ * new provisioning features introduced in API version
+ * {@link android.os.Build.VERSION_CODES#MNC} in addition to those supported in earlier
+ * versions.
+ *
+ * <p>During device owner provisioning a device admin app is set as the owner of the device.
+ * A device owner has full control over the device. The device owner can not be modified by the
+ * user and the only way of resetting the device is if the device owner app calls a factory
+ * reset.
+ *
+ * <p> A typical use case would be a device that is owned by a company, but used by either an
+ * employee or client.
+ *
+ * <p> The NFC message should be sent to an unprovisioned device.
+ *
+ * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
+ * contains the following properties in addition to properties listed at
+ * {@link #MIME_TYPE_PROVISIONING_NFC}:
+ * <ul>
+ * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
+ * Replaces {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. The value of the property
+ * should be converted to a String via
+ * {@link android.content.ComponentName#flattenToString()}</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_BT_MAC_ADDRESS}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_BT_UUID}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_BT_DEVICE_ID}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_BT_USE_PROXY}, optional</li></ul>
*
* <p> When device owner provisioning has completed, an intent of the type
* {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
@@ -483,8 +588,8 @@ public class DevicePolicyManager {
* <p>Input: Nothing.</p>
* <p>Output: Nothing</p>
*/
- public static final String MIME_TYPE_PROVISIONING_NFC
- = "application/com.android.managedprovisioning";
+ public static final String MIME_TYPE_PROVISIONING_NFC_V2
+ = "application/com.android.managedprovisioning.v2";
/**
* Activity action: ask the user to add a new device administrator to the system.
@@ -509,11 +614,10 @@ public class DevicePolicyManager {
/**
* @hide
* Activity action: ask the user to add a new device administrator as the profile owner
- * for this user. Only system privileged apps that have MANAGE_USERS and MANAGE_DEVICE_ADMINS
- * permission can call this API.
+ * for this user. Only system apps can launch this intent.
*
- * <p>The ComponentName of the profile owner admin is pass in {@link #EXTRA_DEVICE_ADMIN} extra
- * field. This will invoke a UI to bring the user through adding the profile owner admin
+ * <p>The ComponentName of the profile owner admin is passed in the {@link #EXTRA_DEVICE_ADMIN}
+ * extra field. This will invoke a UI to bring the user through adding the profile owner admin
* to remotely control restrictions on the user.
*
* <p>The intent must be invoked via {@link Activity#startActivityForResult()} to receive the
@@ -525,8 +629,8 @@ public class DevicePolicyManager {
* field to provide the user with additional explanation (in addition
* to your component's description) about what is being added.
*
- * <p>If there is already a profile owner active or the caller doesn't have the required
- * permissions, the operation will return a failure result.
+ * <p>If there is already a profile owner active or the caller is not a system app, the
+ * operation will return a failure result.
*/
@SystemApi
public static final String ACTION_SET_PROFILE_OWNER
@@ -1941,7 +2045,8 @@ public class DevicePolicyManager {
/**
* Installs the given certificate as a user CA.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Use
+ * <code>null</code> if calling from a delegated certificate installer.
* @param certBuffer encoded form of the certificate to install.
*
* @return false if the certBuffer cannot be parsed or installation is
@@ -1961,7 +2066,8 @@ public class DevicePolicyManager {
/**
* Uninstalls the given certificate from trusted user CAs, if present.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Use
+ * <code>null</code> if calling from a delegated certificate installer.
* @param certBuffer encoded form of the certificate to remove.
*/
public void uninstallCaCert(ComponentName admin, byte[] certBuffer) {
@@ -1982,7 +2088,8 @@ public class DevicePolicyManager {
* If a user has installed any certificates by other means than device policy these will be
* included too.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Use
+ * <code>null</code> if calling from a delegated certificate installer.
* @return a List of byte[] arrays, each encoding one user CA certificate.
*/
public List<byte[]> getInstalledCaCerts(ComponentName admin) {
@@ -2009,7 +2116,8 @@ public class DevicePolicyManager {
* Uninstalls all custom trusted CA certificates from the profile. Certificates installed by
* means other than device policy will also be removed, except for system CA certificates.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Use
+ * <code>null</code> if calling from a delegated certificate installer.
*/
public void uninstallAllUserCaCerts(ComponentName admin) {
if (mService != null) {
@@ -2026,7 +2134,8 @@ public class DevicePolicyManager {
/**
* Returns whether this certificate is installed as a trusted CA.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Use
+ * <code>null</code> if calling from a delegated certificate installer.
* @param certBuffer encoded form of the certificate to look up.
*/
public boolean hasCaCertInstalled(ComponentName admin, byte[] certBuffer) {
@@ -2083,6 +2192,50 @@ public class DevicePolicyManager {
}
/**
+ * Called by a profile owner or device owner to grant access to privileged certificate
+ * manipulation APIs to a third-party CA certificate installer app. Granted APIs include
+ * {@link #getInstalledCaCerts}, {@link #hasCaCertInstalled}, {@link #installCaCert},
+ * {@link #uninstallCaCert} and {@link #uninstallAllUserCaCerts}.
+ * <p>
+ * Delegated certificate installer is a per-user state. The delegated access is persistent until
+ * it is later cleared by calling this method with a null value or uninstallling the certificate
+ * installer.
+ *
+ * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param installerPackage The package name of the certificate installer which will be given
+ * access. If <code>null</code> is given the current package will be cleared.
+ */
+ public void setCertInstallerPackage(ComponentName who, String installerPackage)
+ throws SecurityException {
+ if (mService != null) {
+ try {
+ mService.setCertInstallerPackage(who, installerPackage);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner or device owner to retrieve the certificate installer for the
+ * current user. null if none is set.
+ *
+ * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return The package name of the current delegated certificate installer. <code>null</code>
+ * if none is set.
+ */
+ public String getCertInstallerPackage(ComponentName who) throws SecurityException {
+ if (mService != null) {
+ try {
+ return mService.getCertInstallerPackage(who);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by an application that is administering the device to disable all cameras
* on the device, for this user. After setting this, no applications running as this user
* will be able to access any cameras on the device.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a1f1d92..0a0d77d 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -60,4 +60,13 @@ public abstract class DevicePolicyManagerInternal {
*/
public abstract void addOnCrossProfileWidgetProvidersChangeListener(
OnCrossProfileWidgetProvidersChangeListener listener);
+
+ /**
+ * Checks if an app with given uid is an active device admin of its user and has the policy
+ * specified.
+ * @param uid App uid.
+ * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}.
+ * @return true if the uid is an active admin with the given policy.
+ */
+ public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f69cf36..9ca52e5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,6 +132,9 @@ interface IDevicePolicyManager {
boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias);
void choosePrivateKeyAlias(int uid, in String host, int port, in String url, in String alias, IBinder aliasCallback);
+ void setCertInstallerPackage(in ComponentName who, String installerPackage);
+ String getCertInstallerPackage(in ComponentName who);
+
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
new file mode 100644
index 0000000..af7c053
--- /dev/null
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -0,0 +1,233 @@
+/**
+ * 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.usage;
+
+import android.app.usage.NetworkUsageStats.Bucket;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkIdentity;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * Provides access to network usage history and statistics. Usage data is collected in
+ * discrete bins of time called 'Buckets'. See {@link NetworkUsageStats.Bucket} for details.
+ * <p />
+ * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and
+ * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except
+ * {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user
+ * as the client. In addition tethering usage, usage by removed users and apps, and usage by system
+ * is also included in the results.
+ * <h3>
+ * Summary queries
+ * </h3>
+ * These queries aggregate network usage across the whole interval. Therefore there will be only one
+ * bucket for a particular key and state combination. In case of the user-wide and device-wide
+ * summaries a single bucket containing the totalised network usage is returned.
+ * <h3>
+ * History queries
+ * </h3>
+ * These queries do not aggregate over time but do aggregate over state. Therefore there can be
+ * multiple buckets for a particular key but all Bucket's state is going to be
+ * {@link NetworkUsageStats.Bucket#STATE_ALL}.
+ * <p />
+ * <b>NOTE:</b> This API requires the permission
+ * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and
+ * will not be granted to third-party apps. However, declaring the permission implies intention to
+ * use the API and the user of the device can grant permission through the Settings application.
+ * Profile owner apps are automatically granted permission to query data on the profile they manage
+ * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps likewise get
+ * access to usage data of the primary user.
+ */
+public class NetworkStatsManager {
+ private final static String TAG = "NetworkStatsManager";
+
+ private final Context mContext;
+
+ /**
+ * {@hide}
+ */
+ public NetworkStatsManager(Context context) {
+ mContext = context;
+ }
+ /**
+ * Query network usage statistics summaries. Result is summarised data usage for the whole
+ * device. Result is a single Bucket aggregated over time, state and uid.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Bucket object or null if permissions are insufficient or error happened during
+ * statistics collection.
+ */
+ public Bucket querySummaryForDevice(int networkType, String subscriberId,
+ long startTime, long endTime) throws SecurityException, RemoteException {
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
+ if (template == null) {
+ return null;
+ }
+
+ Bucket bucket = null;
+ NetworkUsageStats stats = new NetworkUsageStats(mContext, template, startTime, endTime);
+ bucket = stats.getDeviceSummaryForNetwork(startTime, endTime);
+
+ stats.close();
+ return bucket;
+ }
+
+ /**
+ * Query network usage statistics summaries. Result is summarised data usage for all uids
+ * belonging to calling user. Result is a single Bucket aggregated over time, state and uid.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Bucket object or null if permissions are insufficient or error happened during
+ * statistics collection.
+ */
+ public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
+ long endTime) throws SecurityException, RemoteException {
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
+ if (template == null) {
+ return null;
+ }
+
+ NetworkUsageStats stats;
+ stats = new NetworkUsageStats(mContext, template, startTime, endTime);
+ stats.startSummaryEnumeration(startTime, endTime);
+
+ stats.close();
+ return stats.getSummaryAggregate();
+ }
+
+ /**
+ * Query network usage statistics summaries. Result filtered to include only uids belonging to
+ * calling user. Result is aggregated over time, hence all buckets will have the same start and
+ * end timestamps. Not aggregated over state or uid.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Statistics object or null if permissions are insufficient or error happened during
+ * statistics collection.
+ */
+ public NetworkUsageStats querySummary(int networkType, String subscriberId, long startTime,
+ long endTime) throws SecurityException, RemoteException {
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
+ if (template == null) {
+ return null;
+ }
+
+ NetworkUsageStats result;
+ result = new NetworkUsageStats(mContext, template, startTime, endTime);
+ result.startSummaryEnumeration(startTime, endTime);
+
+ return result;
+ }
+
+ /**
+ * Query network usage statistics details. Only usable for uids belonging to calling user.
+ * Result is aggregated over state but not aggregated over time.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param uid UID of app
+ * @return Statistics object or null if permissions are insufficient or error happened during
+ * statistics collection.
+ */
+ public NetworkUsageStats queryDetailsForUid(int networkType, String subscriberId,
+ long startTime, long endTime, int uid) throws SecurityException, RemoteException {
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
+ if (template == null) {
+ return null;
+ }
+
+ NetworkUsageStats result;
+ result = new NetworkUsageStats(mContext, template, startTime, endTime);
+ result.startHistoryEnumeration(uid);
+
+ return result;
+ }
+
+ /**
+ * Query network usage statistics details. Result filtered to include only uids belonging to
+ * calling user. Result is aggregated over state but not aggregated over time or uid.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Statistics object or null if permissions are insufficient or error happened during
+ * statistics collection.
+ */
+ public NetworkUsageStats queryDetails(int networkType, String subscriberId, long startTime,
+ long endTime) throws SecurityException, RemoteException {
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
+ if (template == null) {
+ return null;
+ }
+ NetworkUsageStats result;
+ result = new NetworkUsageStats(mContext, template, startTime, endTime);
+ result.startUserUidEnumeration();
+ return result;
+ }
+
+ private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
+ NetworkTemplate template = null;
+ switch (networkType) {
+ case ConnectivityManager.TYPE_MOBILE: {
+ template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ } break;
+ case ConnectivityManager.TYPE_WIFI: {
+ template = NetworkTemplate.buildTemplateWifiWildcard();
+ } break;
+ default: {
+ Log.w(TAG, "Cannot create template for network type " + networkType
+ + ", subscriberId '" + NetworkIdentity.scrubSubscriberId(subscriberId) +
+ "'.");
+ }
+ }
+ return template;
+ }
+}
diff --git a/core/java/android/app/usage/NetworkUsageStats.java b/core/java/android/app/usage/NetworkUsageStats.java
new file mode 100644
index 0000000..990d231
--- /dev/null
+++ b/core/java/android/app/usage/NetworkUsageStats.java
@@ -0,0 +1,479 @@
+/**
+ * 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.usage;
+
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+/**
+ * Class providing enumeration over buckets of network usage statistics. NetworkUsageStats objects
+ * are returned as results to various queries in {@link NetworkStatsManager}.
+ */
+public final class NetworkUsageStats implements AutoCloseable {
+ private final static String TAG = "NetworkUsageStats";
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ /**
+ * Start timestamp of stats collected
+ */
+ private final long mStartTimeStamp;
+
+ /**
+ * End timestamp of stats collected
+ */
+ private final long mEndTimeStamp;
+
+
+ /**
+ * Non-null array indicates the query enumerates over uids.
+ */
+ private int[] mUids;
+
+ /**
+ * Index of the current uid in mUids when doing uid enumeration or a single uid value,
+ * depending on query type.
+ */
+ private int mUidOrUidIndex;
+
+ /**
+ * The session while the query requires it, null if all the stats have been collected or close()
+ * has been called.
+ */
+ private INetworkStatsSession mSession;
+ private NetworkTemplate mTemplate;
+
+ /**
+ * Results of a summary query.
+ */
+ private NetworkStats mSummary = null;
+
+ /**
+ * Results of detail queries.
+ */
+ private NetworkStatsHistory mHistory = null;
+
+ /**
+ * Where we are in enumerating over the current result.
+ */
+ private int mEnumerationIndex = 0;
+
+ /**
+ * Recycling entry objects to prevent heap fragmentation.
+ */
+ private NetworkStats.Entry mRecycledSummaryEntry = null;
+ private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
+
+ /** @hide */
+ NetworkUsageStats(Context context, NetworkTemplate template, long startTimestamp,
+ long endTimestamp) throws RemoteException, SecurityException {
+ final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ // Open network stats session
+ mSession = statsService.openSessionForUsageStats(context.getOpPackageName());
+ mCloseGuard.open("close");
+ mTemplate = template;
+ mStartTimeStamp = startTimestamp;
+ mEndTimeStamp = endTimestamp;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ // -------------------------BEGINNING OF PUBLIC API-----------------------------------
+
+ /**
+ * Buckets are the smallest elements of a query result. As some dimensions of a result may be
+ * aggregated (e.g. time or state) some values may be equal across all buckets.
+ */
+ public static class Bucket {
+ /**
+ * Combined usage across all other states.
+ */
+ public static final int STATE_ALL = -1;
+
+ /**
+ * Usage not accounted in any other states.
+ */
+ public static final int STATE_DEFAULT = 0x1;
+
+ /**
+ * Foreground usage.
+ */
+ public static final int STATE_FOREGROUND = 0x2;
+
+ /**
+ * Special UID value for removed apps.
+ */
+ public static final int UID_REMOVED = -4;
+
+ /**
+ * Special UID value for data usage by tethering.
+ */
+ public static final int UID_TETHERING = -5;
+
+ private int mUid;
+ private int mState;
+ private long mBeginTimeStamp;
+ private long mEndTimeStamp;
+ private long mRxBytes;
+ private long mRxPackets;
+ private long mTxBytes;
+ private long mTxPackets;
+
+ private static int convertState(int networkStatsSet) {
+ switch (networkStatsSet) {
+ case NetworkStats.SET_ALL : return STATE_ALL;
+ case NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
+ case NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
+ }
+ return 0;
+ }
+
+ private static int convertUid(int uid) {
+ switch (uid) {
+ case TrafficStats.UID_REMOVED: return UID_REMOVED;
+ case TrafficStats.UID_TETHERING: return UID_TETHERING;
+ }
+ return uid;
+ }
+
+ public Bucket() {
+ }
+
+ /**
+ * Key of the bucket. Usually an app uid or one of the following special values:<p />
+ * <ul>
+ * <li>{@link #UID_REMOVED}</li>
+ * <li>{@link #UID_TETHERING}</li>
+ * <li>{@link android.os.Process#SYSTEM_UID}</li>
+ * </ul>
+ * @return Bucket key.
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * Usage state. One of the following values:<p/>
+ * <ul>
+ * <li>{@link #STATE_ALL}</li>
+ * <li>{@link #STATE_DEFAULT}</li>
+ * <li>{@link #STATE_FOREGROUND}</li>
+ * </ul>
+ * @return Usage state.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Start of interval.
+ */
+ public long getStartTimeStamp() {
+ return mBeginTimeStamp;
+ }
+
+ /**
+ * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return End of interval.
+ */
+ public long getEndTimeStamp() {
+ return mEndTimeStamp;
+ }
+
+ /**
+ * Number of bytes received during the bucket's time interval. Statistics are measured at
+ * the network layer, so they include both TCP and UDP usage.
+ * @return Number of bytes.
+ */
+ public long getRxBytes() {
+ return mRxBytes;
+ }
+
+ /**
+ * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
+ * the network layer, so they include both TCP and UDP usage.
+ * @return Number of bytes.
+ */
+ public long getTxBytes() {
+ return mTxBytes;
+ }
+
+ /**
+ * Number of packets received during the bucket's time interval. Statistics are measured at
+ * the network layer, so they include both TCP and UDP usage.
+ * @return Number of packets.
+ */
+ public long getRxPackets() {
+ return mRxPackets;
+ }
+
+ /**
+ * Number of packets transmitted during the bucket's time interval. Statistics are measured
+ * at the network layer, so they include both TCP and UDP usage.
+ * @return Number of packets.
+ */
+ public long getTxPackets() {
+ return mTxPackets;
+ }
+ }
+
+ /**
+ * Fills the recycled bucket with data of the next bin in the enumeration.
+ * @param bucketOut Bucket to be filled with data.
+ * @return true if successfully filled the bucket, false otherwise.
+ */
+ public boolean getNextBucket(Bucket bucketOut) {
+ if (mSummary != null) {
+ return getNextSummaryBucket(bucketOut);
+ } else {
+ return getNextHistoryBucket(bucketOut);
+ }
+ }
+
+ /**
+ * Check if it is possible to ask for a next bucket in the enumeration.
+ * @return true if there is at least one more bucket.
+ */
+ public boolean hasNextBucket() {
+ if (mSummary != null) {
+ return mEnumerationIndex < mSummary.size();
+ } else if (mHistory != null) {
+ return mEnumerationIndex < mHistory.size()
+ || hasNextUid();
+ }
+ return false;
+ }
+
+ /**
+ * Closes the enumeration. Call this method before this object gets out of scope.
+ */
+ @Override
+ public void close() {
+ if (mSession != null) {
+ try {
+ mSession.close();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ // Otherwise, meh
+ }
+ }
+ mSession = null;
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
+ }
+
+ // -------------------------END OF PUBLIC API-----------------------------------
+
+ /**
+ * Collects device summary results into a Bucket.
+ * @param startTime
+ * @param endTime
+ * @throws RemoteException
+ */
+ Bucket getDeviceSummaryForNetwork(long startTime, long endTime) throws RemoteException {
+ mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, startTime, endTime);
+
+ // Setting enumeration index beyond end to avoid accidental enumeration over data that does
+ // not belong to the calling user.
+ mEnumerationIndex = mSummary.size();
+
+ return getSummaryAggregate();
+ }
+
+ /**
+ * Collects summary results and sets summary enumeration mode.
+ * @param startTime
+ * @param endTime
+ * @throws RemoteException
+ */
+ void startSummaryEnumeration(long startTime, long endTime) throws RemoteException {
+ mSummary = mSession.getSummaryForAllUid(mTemplate, startTime, endTime, false);
+
+ mEnumerationIndex = 0;
+ }
+
+ /**
+ * Collects history results for uid and resets history enumeration index.
+ */
+ void startHistoryEnumeration(int uid) {
+ mHistory = null;
+ try {
+ mHistory = mSession.getHistoryForUid(mTemplate, uid, NetworkStats.SET_ALL,
+ NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL);
+ setSingleUid(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ // Leaving mHistory null
+ }
+ mEnumerationIndex = 0;
+ }
+
+ /**
+ * Starts uid enumeration for current user.
+ * @throws RemoteException
+ */
+ void startUserUidEnumeration() throws RemoteException {
+ setUidEnumeration(mSession.getRelevantUids());
+ stepHistory();
+ }
+
+ /**
+ * Steps to next uid in enumeration and collects history for that.
+ */
+ private void stepHistory(){
+ if (hasNextUid()) {
+ stepUid();
+ mHistory = null;
+ try {
+ mHistory = mSession.getHistoryForUid(mTemplate, getUid(), NetworkStats.SET_ALL,
+ NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ // Leaving mHistory null
+ }
+ mEnumerationIndex = 0;
+ }
+ }
+
+ private void fillBucketFromSummaryEntry(Bucket bucketOut) {
+ bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
+ bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+ bucketOut.mBeginTimeStamp = mStartTimeStamp;
+ bucketOut.mEndTimeStamp = mEndTimeStamp;
+ bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
+ bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
+ bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
+ bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
+ }
+
+ /**
+ * Getting the next item in summary enumeration.
+ * @param bucketOut Next item will be set here.
+ * @return true if a next item could be set.
+ */
+ private boolean getNextSummaryBucket(Bucket bucketOut) {
+ if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
+ mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
+ fillBucketFromSummaryEntry(bucketOut);
+ return true;
+ }
+ return false;
+ }
+
+ Bucket getSummaryAggregate() {
+ if (mSummary == null) {
+ return null;
+ }
+ Bucket bucket = new Bucket();
+ if (mRecycledSummaryEntry == null) {
+ mRecycledSummaryEntry = new NetworkStats.Entry();
+ }
+ mSummary.getTotal(mRecycledSummaryEntry);
+ fillBucketFromSummaryEntry(bucket);
+ return bucket;
+ }
+ /**
+ * Getting the next item in a history enumeration.
+ * @param bucketOut Next item will be set here.
+ * @return true if a next item could be set.
+ */
+ private boolean getNextHistoryBucket(Bucket bucketOut) {
+ if (bucketOut != null && mHistory != null) {
+ if (mEnumerationIndex < mHistory.size()) {
+ mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
+ mRecycledHistoryEntry);
+ bucketOut.mUid = Bucket.convertUid(getUid());
+ bucketOut.mState = Bucket.STATE_ALL;
+ bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
+ bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
+ mRecycledHistoryEntry.bucketDuration;
+ bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
+ bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
+ bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
+ bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
+ return true;
+ } else if (hasNextUid()) {
+ stepHistory();
+ return getNextHistoryBucket(bucketOut);
+ }
+ }
+ return false;
+ }
+
+ // ------------------ UID LOGIC------------------------
+
+ private boolean isUidEnumeration() {
+ return mUids != null;
+ }
+
+ private boolean hasNextUid() {
+ return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
+ }
+
+ private int getUid() {
+ // Check if uid enumeration.
+ if (isUidEnumeration()) {
+ if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
+ throw new IndexOutOfBoundsException(
+ "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
+ }
+ return mUids[mUidOrUidIndex];
+ }
+ // Single uid mode.
+ return mUidOrUidIndex;
+ }
+
+ private void setSingleUid(int uid) {
+ mUidOrUidIndex = uid;
+ }
+
+ private void setUidEnumeration(int[] uids) {
+ mUids = uids;
+ mUidOrUidIndex = -1;
+ }
+
+ private void stepUid() {
+ if (mUids != null) {
+ ++mUidOrUidIndex;
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 7eae439..0106686 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -25,6 +25,13 @@ import android.os.Parcelable;
* parameters for the scan.
*/
public final class ScanSettings implements Parcelable {
+
+ /**
+ * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
+ * other scan results without starting BLE scans themselves.
+ */
+ public static final int SCAN_MODE_OPPORTUNISTIC = -1;
+
/**
* Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
* least power.
@@ -177,7 +184,7 @@ public final class ScanSettings implements Parcelable {
* @throws IllegalArgumentException If the {@code scanMode} is invalid.
*/
public Builder setScanMode(int scanMode) {
- if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+ if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
throw new IllegalArgumentException("invalid scan mode " + scanMode);
}
mScanMode = scanMode;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b5e0b..e9d4e59 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2149,7 +2149,7 @@ public abstract class Context {
CONNECTIVITY_SERVICE,
//@hide: UPDATE_LOCK_SERVICE,
//@hide: NETWORKMANAGEMENT_SERVICE,
- //@hide: NETWORK_STATS_SERVICE,
+ NETWORK_STATS_SERVICE,
//@hide: NETWORK_POLICY_SERVICE,
WIFI_SERVICE,
WIFI_PASSPOINT_SERVICE,
@@ -2259,6 +2259,9 @@ public abstract class Context {
* <dd> A {@link android.os.BatteryManager} for managing battery state
* <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")
* <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks
+ * <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")
+ * <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network
+ * usage statistics.
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -2316,6 +2319,8 @@ public abstract class Context {
* @see android.os.BatteryManager
* @see #JOB_SCHEDULER_SERVICE
* @see android.app.job.JobScheduler
+ * @see #NETWORK_STATS_SERVICE
+ * @see android.app.usage.NetworkStatsManager
*/
public abstract Object getSystemService(@ServiceName @NonNull String name);
@@ -2334,7 +2339,8 @@ public abstract class Context {
* {@link android.telephony.TelephonyManager}, {@link android.telephony.SubscriptionManager},
* {@link android.view.inputmethod.InputMethodManager},
* {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
- * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler}.
+ * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
+ * {@link android.app.usage.NetworkStatsManager}.
* </p><p>
* Note: System services obtained via this API may be closely associated with
* the Context in which they are obtained from. In general, do not share the
@@ -2563,7 +2569,13 @@ public abstract class Context {
*/
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
- /** {@hide} */
+ /**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.usage.NetworkStatsManager} for querying network usage stats.
+ *
+ * @see #getSystemService
+ * @see android.app.usage.NetworkStatsManager
+ */
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
@@ -2819,7 +2831,7 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.bluetooth.BluetoothAdapter} for using Bluetooth.
+ * {@link android.bluetooth.BluetoothManager} for using Bluetooth.
*
* @see #getSystemService
*/
@@ -3109,6 +3121,20 @@ public abstract class Context {
public abstract int checkCallingOrSelfPermission(@NonNull String permission);
/**
+ * Determine whether <em>you</em> have been granted a particular permission.
+ *
+ * @param permission The name of the permission being checked.
+ *
+ * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+ * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+ *
+ * @see PackageManager#checkPermission(String, String)
+ * @see #checkCallingPermission(String)
+ */
+ @PackageManager.PermissionResult
+ public abstract int checkSelfPermission(@NonNull String permission);
+
+ /**
* If the given permission is not allowed for a particular process
* and user ID running in the system, throw a {@link SecurityException}.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6e8b7c1..8c5a87c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -602,6 +602,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public int checkSelfPermission(String permission) {
+ return mBase.checkSelfPermission(permission);
+ }
+
+ @Override
public void enforcePermission(
String permission, int pid, int uid, String message) {
mBase.enforcePermission(permission, pid, uid, message);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f685475..030b770 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1502,6 +1502,66 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+ /**
+ * Activity action: Launch UI to manage the permissions of an app.
+ * <p>
+ * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permissions
+ * will be managed by the launched UI.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @see #EXTRA_PACKAGE_NAME
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_APP_PERMISSIONS =
+ "android.intent.action.MANAGE_APP_PERMISSIONS";
+
+ /**
+ * Intent extra: An app package name.
+ * <p>
+ * Type: String
+ * </p>S
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+
+ /**
+ * Activity action: Launch UI to manage which apps have a given permission.
+ * <p>
+ * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
+ * to which will be managed by the launched UI.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @see #EXTRA_PERMISSION_NAME
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_PERMISSION_APPS =
+ "android.intent.action.MANAGE_PERMISSION_APPS";
+
+ /**
+ * Intent extra: The name of a permission.
+ * <p>
+ * Type: String
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent broadcast actions (see action variable).
@@ -1519,6 +1579,10 @@ public class Intent implements Parcelable, Cloneable {
* </p><p>
* See {@link android.os.PowerManager#isInteractive} for details.
* </p>
+ * You <em>cannot</em> receive this through components declared in
+ * manifests, only by explicitly registering for it with
+ * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+ * Context.registerReceiver()}.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
@@ -1539,6 +1603,10 @@ public class Intent implements Parcelable, Cloneable {
* </p><p>
* See {@link android.os.PowerManager#isInteractive} for details.
* </p>
+ * You <em>cannot</em> receive this through components declared in
+ * manifests, only by explicitly registering for it with
+ * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+ * Context.registerReceiver()}.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
@@ -1576,7 +1644,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast Action: The current time has changed. Sent every
- * minute. You can <em>not</em> receive this through components declared
+ * minute. You <em>cannot</em> receive this through components declared
* in manifests, only by explicitly registering for it with
* {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver()}.
@@ -1922,7 +1990,7 @@ public class Intent implements Parcelable, Cloneable {
* appropriately.
*
* <p class="note">
- * You can <em>not</em> receive this through components declared
+ * You <em>cannot</em> receive this through components declared
* in manifests, only by explicitly registering for it with
* {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver()}.
@@ -1949,7 +2017,7 @@ public class Intent implements Parcelable, Cloneable {
* contents of the Intent.
*
* <p class="note">
- * You can <em>not</em> receive this through components declared
+ * You <em>cannot</em> receive this through components declared
* in manifests, only by explicitly registering for it with
* {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver()}. See {@link #ACTION_BATTERY_LOW},
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
index 1d5ed8a..46c2c6e 100644
--- a/core/java/android/content/UndoManager.java
+++ b/core/java/android/content/UndoManager.java
@@ -119,15 +119,13 @@ public class UndoManager {
}
/**
- * Flatten the current undo state into a Parcelable object, which can later be restored
- * with {@link #restoreInstanceState(android.os.Parcelable)}.
+ * Flatten the current undo state into a Parcel object, which can later be restored
+ * with {@link #restoreInstanceState(android.os.Parcel, java.lang.ClassLoader)}.
*/
- public Parcelable saveInstanceState() {
+ public void saveInstanceState(Parcel p) {
if (mUpdateCount > 0) {
throw new IllegalStateException("Can't save state while updating");
}
- ParcelableParcel pp = new ParcelableParcel(getClass().getClassLoader());
- Parcel p = pp.getParcel();
mStateSeq++;
if (mStateSeq <= 0) {
mStateSeq = 0;
@@ -151,7 +149,6 @@ public class UndoManager {
mRedos.get(i).writeToParcel(p);
}
p.writeInt(0);
- return pp;
}
void saveOwner(UndoOwner owner, Parcel out) {
@@ -168,26 +165,24 @@ public class UndoManager {
}
/**
- * Restore an undo state previously created with {@link #saveInstanceState()}. This will
- * restore the UndoManager's state to almost exactly what it was at the point it had
+ * Restore an undo state previously created with {@link #saveInstanceState(Parcel)}. This
+ * will restore the UndoManager's state to almost exactly what it was at the point it had
* been previously saved; the only information not restored is the data object
* associated with each {@link UndoOwner}, which requires separate calls to
* {@link #getOwner(String, Object)} to re-associate the owner with its data.
*/
- public void restoreInstanceState(Parcelable state) {
+ public void restoreInstanceState(Parcel p, ClassLoader loader) {
if (mUpdateCount > 0) {
throw new IllegalStateException("Can't save state while updating");
}
forgetUndos(null, -1);
forgetRedos(null, -1);
- ParcelableParcel pp = (ParcelableParcel)state;
- Parcel p = pp.getParcel();
mHistorySize = p.readInt();
mStateOwners = new UndoOwner[p.readInt()];
int stype;
while ((stype=p.readInt()) != 0) {
- UndoState ustate = new UndoState(this, p, pp.getClassLoader());
+ UndoState ustate = new UndoState(this, p, loader);
if (stype == 1) {
mUndos.add(0, ustate);
} else {
@@ -311,12 +306,15 @@ public class UndoManager {
}
int removed = 0;
- for (int i=0; i<mUndos.size() && removed < count; i++) {
+ int i = 0;
+ while (i < mUndos.size() && removed < count) {
UndoState state = mUndos.get(i);
if (count > 0 && matchOwners(state, owners)) {
state.destroy();
mUndos.remove(i);
removed++;
+ } else {
+ i++;
}
}
@@ -329,12 +327,15 @@ public class UndoManager {
}
int removed = 0;
- for (int i=0; i<mRedos.size() && removed < count; i++) {
+ int i = 0;
+ while (i < mRedos.size() && removed < count) {
UndoState state = mRedos.get(i);
if (count > 0 && matchOwners(state, owners)) {
state.destroy();
mRedos.remove(i);
removed++;
+ } else {
+ i++;
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e1a2aa9..2496e45 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -334,6 +334,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
/**
+ * Value for {@link #flags}: {@code true} if the application may use cleartext network traffic
+ * (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.
+ */
+ public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
+
+ /**
+ * When set installer extracts native libs from .apk files.
+ */
+ public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28;
+
+ /**
* Value for {@link #flags}: true if code from this application will need to be
* loaded into other applications' processes. On devices that support multiple
* instruction sets, this implies the code might be loaded into a process that's
@@ -362,7 +377,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@link #FLAG_LARGE_HEAP}, {@link #FLAG_STOPPED},
* {@link #FLAG_SUPPORTS_RTL}, {@link #FLAG_INSTALLED},
* {@link #FLAG_IS_DATA_ONLY}, {@link #FLAG_IS_GAME},
- * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_MULTIARCH}.
+ * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_USES_CLEARTEXT_TRAFFIC},
+ * {@link #FLAG_MULTIARCH}.
*/
public int flags = 0;
@@ -598,6 +614,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ /**
+ * True when the application's rendering should be hardware accelerated.
+ */
+ public boolean hardwareAccelerated;
+
public void dump(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
if (className != null) {
@@ -633,10 +654,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
pw.println(prefix + "dataDir=" + dataDir);
if (sharedLibraryFiles != null) {
- pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
+ pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode);
+ pw.println(prefix + "hardwareAccelerated=" + hardwareAccelerated);
if (manageSpaceActivityName != null) {
pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
}
@@ -722,6 +744,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
descriptionRes = orig.descriptionRes;
uiOptions = orig.uiOptions;
backupAgentName = orig.backupAgentName;
+ hardwareAccelerated = orig.hardwareAccelerated;
}
@@ -773,6 +796,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeString(backupAgentName);
dest.writeInt(descriptionRes);
dest.writeInt(uiOptions);
+ dest.writeInt(hardwareAccelerated ? 1 : 0);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -823,6 +847,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
backupAgentName = source.readString();
descriptionRes = source.readInt();
uiOptions = source.readInt();
+ hardwareAccelerated = source.readInt() != 0;
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3e5d362..c6d97f1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -46,32 +46,34 @@ import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
+import com.android.internal.os.IResultReceiver;
/**
* See {@link PackageManager} for documentation on most of the APIs
* here.
- *
+ *
* {@hide}
*/
interface IPackageManager {
boolean isPackageAvailable(String packageName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
int getPackageUid(String packageName, int userId);
- int[] getPackageGids(String packageName);
-
+ int[] getPackageGids(String packageName, int userId);
+
String[] currentToCanonicalPackageNames(in String[] names);
String[] canonicalToCurrentPackageNames(in String[] names);
PermissionInfo getPermissionInfo(String name, int flags);
-
+
List<PermissionInfo> queryPermissionsByGroup(String group, int flags);
-
+
PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
-
+
List<PermissionGroupInfo> getAllPermissionGroups(int flags);
-
+
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
@@ -85,28 +87,28 @@ interface IPackageManager {
ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
- int checkPermission(String permName, String pkgName);
-
+ int checkPermission(String permName, String pkgName, int userId);
+
int checkUidPermission(String permName, int uid);
-
+
boolean addPermission(in PermissionInfo info);
-
+
void removePermission(String name);
- void grantPermission(String packageName, String permissionName);
+ boolean grantPermission(String packageName, String permissionName, int userId);
- void revokePermission(String packageName, String permissionName);
+ boolean revokePermission(String packageName, String permissionName, int userId);
boolean isProtectedBroadcast(String actionName);
-
+
int checkSignatures(String pkg1, String pkg2);
-
+
int checkUidSignatures(int uid1, int uid2);
-
+
String[] getPackagesForUid(int uid);
-
+
String getNameForUid(int uid);
-
+
int getUidForSharedUser(String sharedUserName);
int getFlagsForUid(int uid);
@@ -121,7 +123,7 @@ interface IPackageManager {
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
- List<ResolveInfo> queryIntentActivities(in Intent intent,
+ List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentActivityOptions(
@@ -168,7 +170,7 @@ interface IPackageManager {
/**
* Retrieve all applications that are marked as persistent.
- *
+ *
* @return A List&lt;applicationInfo> containing one entry for each persistent
* application.
*/
@@ -178,7 +180,7 @@ interface IPackageManager {
/**
* Retrieve sync information for all content providers.
- *
+ *
* @param outNames Filled in with a list of the root names of the content
* providers that can sync.
* @param outInfo Filled in with a list of the ProviderInfo for each
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9223269..9e6c6b5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -167,8 +167,7 @@ public class PackageInfo implements Parcelable {
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
- * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
- * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
*/
public int[] requestedPermissionsFlags;
@@ -176,6 +175,8 @@ public class PackageInfo implements Parcelable {
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
+ *
+ * @removed We do not support required permissions.
*/
public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0365689..59a16da 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
+import com.android.internal.util.ArrayUtils;
import java.io.File;
import java.lang.annotation.Retention;
@@ -377,6 +378,16 @@ public abstract class PackageManager {
public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
/**
+ * Flag parameter for {@link #installPackage} to indicate that all runtime
+ * permissions should be granted to the package. If {@link #INSTALL_ALL_USERS}
+ * is set the runtime permissions will be granted to all users, otherwise
+ * only to the owner.
+ *
+ * @hide
+ */
+ public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -1663,21 +1674,46 @@ public abstract class PackageManager {
= "android.content.pm.extra.VERIFICATION_VERSION_CODE";
/**
- * The action used to request that the user approve a permission request
- * from the application.
+ * The action used to request that the user approve a grant permissions
+ * request from the application.
*
* @hide
*/
- public static final String ACTION_REQUEST_PERMISSION
- = "android.content.pm.action.REQUEST_PERMISSION";
+ @SystemApi
+ public static final String ACTION_REQUEST_PERMISSIONS =
+ "android.content.pm.action.REQUEST_PERMISSIONS";
/**
- * Extra field name for the list of permissions, which the user must approve.
+ * The component name handling runtime permission grants.
*
* @hide
*/
- public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST
- = "android.content.pm.extra.PERMISSION_LIST";
+ public static final String GRANT_PERMISSIONS_PACKAGE_NAME =
+ "com.android.packageinstaller";
+
+ /**
+ * The names of the requested permissions.
+ * <p>
+ * <strong>Type:</strong> String[]
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
+ "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+
+ /**
+ * The results from the permissions request.
+ * <p>
+ * <strong>Type:</strong> int[] of #PermissionResult
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS
+ = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
/**
* String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
@@ -2184,51 +2220,70 @@ public abstract class PackageManager {
public abstract void removePermission(String name);
/**
- * Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
- * which prompts the user to grant {@code permissions} to this application.
- * @hide
+ * Grant a runtime permission to an application which the application does not
+ * already have. The permission must have been requested by the application.
+ * If the application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
+ *
+ * @param packageName The package to which to grant the permission.
+ * @param permissionName The permission name to grant.
+ * @param user The user for which to grant the permission.
*
- * @throws NullPointerException if {@code permissions} is {@code null}.
- * @throws IllegalArgumentException if {@code permissions} contains {@code null}.
+ * @see #revokePermission(String, String, android.os.UserHandle)
+ *
+ * @hide
*/
- public Intent buildPermissionRequestIntent(String... permissions) {
- if (permissions == null) {
- throw new NullPointerException("permissions cannot be null");
- }
- for (String permission : permissions) {
- if (permission == null) {
- throw new IllegalArgumentException("permissions cannot contain null");
- }
- }
-
- Intent i = new Intent(ACTION_REQUEST_PERMISSION);
- i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions);
- i.setPackage("com.android.packageinstaller");
- return i;
- }
+ @SystemApi
+ public abstract void grantPermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
/**
- * Grant a permission to an application which the application does not
- * already have. The permission must have been requested by the application,
- * but as an optional permission. If the application is not allowed to
- * hold the permission, a SecurityException is thrown.
- * @hide
+ * Revoke a runtime permission that was previously granted by {@link
+ * #grantPermission(String, String, android.os.UserHandle)}. The permission
+ * must have been requested by and granted to the application. If the
+ * application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
+ *
+ * @param packageName The package from which to revoke the permission.
+ * @param permissionName The permission name to revoke.
+ * @param user The user for which to revoke the permission.
*
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
+ * @see #grantPermission(String, String, android.os.UserHandle)
+ *
+ * @hide
*/
- public abstract void grantPermission(String packageName, String permissionName);
+ @SystemApi
+ public abstract void revokePermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
/**
- * Revoke a permission that was previously granted by {@link #grantPermission}.
- * @hide
+ * Returns an {@link android.content.Intent} suitable for passing to
+ * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+ * which prompts the user to grant permissions to this application.
+ *
+ * @throws NullPointerException if {@code permissions} is {@code null} or empty.
*
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
+ * @hide
*/
- public abstract void revokePermission(String packageName, String permissionName);
+ public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
+ if (ArrayUtils.isEmpty(permissions)) {
+ throw new NullPointerException("permission cannot be null or empty");
+ }
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
+ intent.setPackage(GRANT_PERMISSIONS_PACKAGE_NAME);
+ return intent;
+ }
/**
* Compare the signatures of two packages to determine if the same
@@ -3692,11 +3747,11 @@ public abstract class PackageManager {
* {@link #addPreferredActivity}, that are
* currently registered with the system.
*
- * @param outFilters A list in which to place the filters of all of the
- * preferred activities, or null for none.
- * @param outActivities A list in which to place the component names of
- * all of the preferred activities, or null for none.
- * @param packageName An option package in which you would like to limit
+ * @param outFilters A required list in which to place the filters of all of the
+ * preferred activities.
+ * @param outActivities A required list in which to place the component names of
+ * all of the preferred activities.
+ * @param packageName An optional package in which you would like to limit
* the list. If null, all activities will be returned; if non-null, only
* those activities in the given package are returned.
*
@@ -3704,8 +3759,8 @@ public abstract class PackageManager {
* (the number of distinct IntentFilter records, not the number of unique
* activity components) that were found.
*/
- public abstract int getPreferredActivities(List<IntentFilter> outFilters,
- List<ComponentName> outActivities, String packageName);
+ public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
+ @NonNull List<ComponentName> outActivities, String packageName);
/**
* Ask for the set of available 'home' activities and the current explicit
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1140756..212cf6d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -269,6 +269,7 @@ public class PackageParser {
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths, int[] splitRevisionCodes) {
@@ -284,6 +285,7 @@ public class PackageParser {
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
+ this.extractNativeLibs = baseApk.extractNativeLibs;
}
public List<String> getAllCodePaths() {
@@ -310,10 +312,12 @@ public class PackageParser {
public final Signature[] signatures;
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, boolean coreApp, boolean multiArch) {
+ Signature[] signatures, boolean coreApp, boolean multiArch,
+ boolean extractNativeLibs) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -324,6 +328,7 @@ public class PackageParser {
this.signatures = signatures;
this.coreApp = coreApp;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
}
}
@@ -371,16 +376,6 @@ public class PackageParser {
return path.endsWith(".apk");
}
- /*
- public static PackageInfo generatePackageInfo(PackageParser.Package p,
- int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions) {
- PackageUserState state = new PackageUserState();
- return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, state, UserHandle.getCallingUserId());
- }
- */
-
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -389,7 +384,7 @@ public class PackageParser {
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- ArraySet<String> grantedPermissions, PackageUserState state) {
+ Set<String> grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
@@ -410,7 +405,7 @@ public class PackageParser {
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- ArraySet<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, PackageUserState state, int userId) {
if (!checkUseInstalledOrHidden(flags, state)) {
return null;
@@ -569,9 +564,8 @@ public class PackageParser {
for (int i=0; i<N; i++) {
final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
- if (p.requestedPermissionsRequired.get(i)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
- }
+ // The notion of required permissions is deprecated but for compatibility.
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
@@ -1270,6 +1264,7 @@ public class PackageParser {
int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
+ boolean extractNativeLibs = true;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1308,14 +1303,17 @@ public class PackageParser {
final String attr = attrs.getAttributeName(i);
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
- break;
+ }
+ if ("extractNativeLibs".equals(attr)) {
+ extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
}
}
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
+ revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
+ extractNativeLibs);
}
/**
@@ -1812,7 +1810,6 @@ public class PackageParser {
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
if (implicitPerms != null) {
@@ -1831,7 +1828,6 @@ public class PackageParser {
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
}
@@ -1865,17 +1861,6 @@ public class PackageParser {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
- /*
- * b/8528162: Ignore the <uses-permission android:required> attribute if
- * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
- * which are improperly using this attribute, even though it never worked.
- */
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
- for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
- pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
- }
- }
-
return pkg;
}
@@ -1911,11 +1896,6 @@ public class PackageParser {
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
-/*
- boolean required = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-*/
- boolean required = true; // Optional <uses-permission> not supported
int maxSdkVersion = 0;
TypedValue val = sa.peekValue(
@@ -1933,13 +1913,9 @@ public class PackageParser {
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
- pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
} else {
- if (pkg.requestedPermissionsRequired.get(index) != required) {
- outError[0] = "conflicting <uses-permission> entries";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return false;
- }
+ Slog.w(TAG, "Ignoring duplicate uses-permission: " + name + " in package: "
+ + pkg.packageName + " at: " + parser.getPositionDescription());
}
}
}
@@ -2519,6 +2495,7 @@ public class PackageParser {
owner.baseHardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
+ ai.hardwareAccelerated = owner.baseHardwareAccelerated;
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
@@ -2551,6 +2528,12 @@ public class PackageParser {
}
if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
+ true)) {
+ ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
+ }
+
+ if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
false /* default is no RTL support*/)) {
ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
@@ -2562,6 +2545,12 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
+ true)) {
+ ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
@@ -3109,8 +3098,7 @@ public class PackageParser {
}
a.info.resizeable = sa.getBoolean(
- R.styleable.AndroidManifestActivity_resizeableActivity,
- owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.MNC);
+ R.styleable.AndroidManifestActivity_resizeableActivity, false);
if (a.info.resizeable) {
// Fixed screen orientation isn't supported with resizeable activities.
a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -4211,7 +4199,6 @@ public class PackageParser {
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
- public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
public ArrayList<String> protectedBroadcasts;
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 841b09d..7d8dff3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -464,6 +464,33 @@ public class ColorStateList implements Parcelable {
return mColors;
}
+ /**
+ * Returns whether the specified state is referenced in any of the state
+ * specs contained within this ColorStateList.
+ * <p>
+ * Any reference, either positive or negative {ex. ~R.attr.state_enabled},
+ * will cause this method to return {@code true}. Wildcards are not counted
+ * as references.
+ *
+ * @param state the state to search for
+ * @return {@code true} if the state if referenced, {@code false} otherwise
+ * @hide Use only as directed. For internal use only.
+ */
+ public boolean hasState(int state) {
+ final int[][] stateSpecs = mStateSpecs;
+ final int specCount = stateSpecs.length;
+ for (int specIndex = 0; specIndex < specCount; specIndex++) {
+ final int[] states = stateSpecs[specIndex];
+ final int stateCount = states.length;
+ for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
+ if (states[stateIndex] == state || states[stateIndex] == ~state) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
return "ColorStateList{" +
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 5dc9ef9..44018ff 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -111,12 +111,12 @@ public class Resources {
// single-threaded, and after that these are immutable.
private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
- = new LongSparseArray<ConstantState>();
+ = new LongSparseArray<>();
private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists
- = new LongSparseArray<ColorStateListFactory>();
+ = new LongSparseArray<>();
// Pool of TypedArrays targeted to this Resources object.
- final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<TypedArray>(5);
+ final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
// Used by BridgeResources in layoutlib
static Resources mSystem = null;
@@ -128,21 +128,19 @@ public class Resources {
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+ new ArrayMap<>();
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+ new ArrayMap<>();
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
- new ConfigurationBoundResourceCache<ColorStateList>(this);
+ new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
- new ConfigurationBoundResourceCache<Animator>(this);
+ new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
- new ConfigurationBoundResourceCache<StateListAnimator>(this);
+ new ConfigurationBoundResourceCache<>(this);
private TypedValue mTmpValue = new TypedValue();
private boolean mPreloading;
- private TypedArray mCachedStyledAttributes = null;
-
private int mLastCachedXmlBlockIndex = -1;
private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
@@ -157,8 +155,8 @@ public class Resources {
static {
sPreloadedDrawables = new LongSparseArray[2];
- sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
- sPreloadedDrawables[1] = new LongSparseArray<ConstantState>();
+ sPreloadedDrawables[0] = new LongSparseArray<>();
+ sPreloadedDrawables[1] = new LongSparseArray<>();
}
/**
@@ -927,6 +925,7 @@ public class Resources {
* @deprecated Use {@link #getColor(int, Theme)} instead.
*/
@ColorInt
+ @Deprecated
public int getColor(@ColorRes int id) throws NotFoundException {
return getColor(id, null);
}
@@ -996,6 +995,7 @@ public class Resources {
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
@Nullable
+ @Deprecated
public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
if (csl != null && csl.canApplyTheme()) {
@@ -1049,8 +1049,6 @@ public class Resources {
return res;
}
-
-
/**
* Return a boolean associated with a particular resource ID. This can be
* used with any integral resource value, and will return true if it is
@@ -1876,7 +1874,7 @@ public class Resources {
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
- int configChanges = calcConfigChanges(config);
+ final int configChanges = calcConfigChanges(config);
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
mConfiguration.setLayoutDirection(mConfiguration.locale);
@@ -1891,7 +1889,8 @@ public class Resources {
if (mConfiguration.locale != null) {
locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
}
- int width, height;
+
+ final int width, height;
if (mMetrics.widthPixels >= mMetrics.heightPixels) {
width = mMetrics.widthPixels;
height = mMetrics.heightPixels;
@@ -1901,12 +1900,15 @@ public class Resources {
//noinspection SuspiciousNameCombination
height = mMetrics.widthPixels;
}
- int keyboardHidden = mConfiguration.keyboardHidden;
- if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
- && mConfiguration.hardKeyboardHidden
- == Configuration.HARDKEYBOARDHIDDEN_YES) {
+
+ final int keyboardHidden;
+ if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+ && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+ } else {
+ keyboardHidden = mConfiguration.keyboardHidden;
}
+
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
locale, mConfiguration.orientation,
mConfiguration.touchscreen,
@@ -2508,10 +2510,10 @@ public class Resources {
// Clean out the caches before we add more. This shouldn't
// happen very often.
pruneCaches(caches);
- themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
+ themedCache = new LongSparseArray<>(1);
caches.put(themeKey, themedCache);
}
- themedCache.put(key, new WeakReference<ConstantState>(cs));
+ themedCache.put(key, new WeakReference<>(cs));
}
}
}
@@ -2830,15 +2832,6 @@ public class Resources {
+ Integer.toHexString(id));
}
- /*package*/ void recycleCachedStyledAttributes(TypedArray attrs) {
- synchronized (mAccessLock) {
- final TypedArray cached = mCachedStyledAttributes;
- if (cached == null || cached.mData.length < attrs.mData.length) {
- mCachedStyledAttributes = attrs;
- }
- }
- }
-
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 9652db7..5cfc41f 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -328,7 +328,7 @@ final class StringBlock {
String name = color.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes);
+ ColorStateList colors = res.getColorStateList(colorRes, null);
if (foreground) {
return new TextAppearanceSpan(null, 0, 0, colors, null);
} else {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 9aede01..d5dfaf6 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -18,8 +18,6 @@ package android.hardware;
import android.hardware.ICamera;
import android.hardware.ICameraClient;
-import android.hardware.IProCameraUser;
-import android.hardware.IProCameraCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -45,12 +43,6 @@ interface ICameraService
// Container for an ICamera object
out BinderHolder device);
- int connectPro(IProCameraCallbacks callbacks, int cameraId,
- String clientPackageName,
- int clientUid,
- // Container for an IProCameraUser object
- out BinderHolder device);
-
int connectDevice(ICameraDeviceCallbacks callbacks, int cameraId,
String clientPackageName,
int clientUid,
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index a6c3ea4..88fa339 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -54,11 +54,13 @@ public class SystemSensorManager extends SensorManager {
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
private final int mTargetSdkLevel;
+ private final String mPackageName;
/** {@hide} */
public SystemSensorManager(Context context, Looper mainLooper) {
mMainLooper = mainLooper;
mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
+ mPackageName = context.getPackageName();
synchronized(sSensorModuleLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
@@ -117,14 +119,14 @@ public class SystemSensorManager extends SensorManager {
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
queue = new SensorEventQueue(listener, looper, this);
- if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) {
+ if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
queue.dispose();
return false;
}
mSensorListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags);
+ return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);
}
}
}
@@ -165,14 +167,14 @@ public class SystemSensorManager extends SensorManager {
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
queue = new TriggerEventQueue(listener, mMainLooper, this);
- if (!queue.addSensor(sensor, 0, 0, 0)) {
+ if (!queue.addSensor(sensor, 0, 0)) {
queue.dispose();
return false;
}
mTriggerListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, 0, 0, 0);
+ return queue.addSensor(sensor, 0, 0);
}
}
}
@@ -223,9 +225,9 @@ public class SystemSensorManager extends SensorManager {
*/
private static abstract class BaseEventQueue {
private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
- float[] scratch);
+ float[] scratch, String packageName);
private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
- int maxBatchReportLatencyUs, int reservedFlags);
+ int maxBatchReportLatencyUs);
private static native int nativeDisableSensor(long eventQ, int handle);
private static native void nativeDestroySensorEventQueue(long eventQ);
private static native int nativeFlushSensor(long eventQ);
@@ -238,7 +240,8 @@ public class SystemSensorManager extends SensorManager {
protected final SystemSensorManager mManager;
BaseEventQueue(Looper looper, SystemSensorManager manager) {
- nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
+ nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch,
+ manager.mPackageName);
mCloseGuard.open("dispose");
mManager = manager;
}
@@ -248,7 +251,7 @@ public class SystemSensorManager extends SensorManager {
}
public boolean addSensor(
- Sensor sensor, int delayUs, int maxBatchReportLatencyUs, int reservedFlags) {
+ Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {
// Check if already present.
int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) return false;
@@ -256,10 +259,10 @@ public class SystemSensorManager extends SensorManager {
// Get ready to receive events before calling enable.
mActiveSensors.put(handle, true);
addSensorEvent(sensor);
- if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags) != 0) {
+ if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
// Try continuous mode if batching fails.
if (maxBatchReportLatencyUs == 0 ||
- maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0, 0) != 0) {
+ maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
removeSensor(sensor, false);
return false;
}
@@ -328,11 +331,11 @@ public class SystemSensorManager extends SensorManager {
}
private int enableSensor(
- Sensor sensor, int rateUs, int maxBatchReportLatencyUs, int reservedFlags) {
+ Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
- maxBatchReportLatencyUs, reservedFlags);
+ maxBatchReportLatencyUs);
}
private int disableSensor(Sensor sensor) {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index faa782a..b513379 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1013,10 +1013,10 @@ public final class CameraManager {
if (DEBUG) {
Log.v(TAG,
String.format(
- "Device status was previously available (%d), " +
- " and is now again available (%d)" +
+ "Device status was previously available (%b), " +
+ " and is now again available (%b)" +
"so no new client visible update will be sent",
- isAvailable(status), isAvailable(status)));
+ isAvailable(oldStatus), isAvailable(status)));
}
return;
}
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index d286d38..01f2396 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -16,7 +16,7 @@
package android.hardware.camera2;
-import android.view.Surface;
+import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.CaptureRequest;
@@ -66,7 +66,7 @@ interface ICameraDeviceUser
int deleteStream(int streamId);
// non-negative value is the stream ID. negative value is status_t
- int createStream(in Surface surface);
+ int createStream(in OutputConfiguration outputConfiguration);
int createDefaultRequest(int templateId, out CameraMetadataNative request);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index c5267cb..38f8e39 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -79,7 +79,8 @@ public class CameraDeviceImpl extends CameraDevice {
private int mRepeatingRequestId = REQUEST_ID_NONE;
private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
// Map stream IDs to Surfaces
- private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
+ private final SparseArray<OutputConfiguration> mConfiguredOutputs =
+ new SparseArray<OutputConfiguration>();
private final String mCameraId;
private final CameraCharacteristics mCharacteristics;
@@ -315,7 +316,11 @@ public class CameraDeviceImpl extends CameraDevice {
public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
// Leave this here for backwards compatibility with older code using this directly
- configureOutputsChecked(outputs);
+ ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
+ for (Surface s : outputs) {
+ outputConfigs.add(new OutputConfiguration(s));
+ }
+ configureOutputsChecked(outputConfigs);
}
/**
@@ -334,28 +339,30 @@ public class CameraDeviceImpl extends CameraDevice {
*
* @throws CameraAccessException if there were any unexpected problems during configuration
*/
- public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
+ public boolean configureOutputsChecked(List<OutputConfiguration> outputs)
+ throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
- outputs = new ArrayList<Surface>();
+ outputs = new ArrayList<OutputConfiguration>();
}
boolean success = false;
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
-
- HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
- List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
+ // Streams to create
+ HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
+ // Streams to delete
+ List<Integer> deleteList = new ArrayList<Integer>();
// Determine which streams need to be created, which to be deleted
for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
int streamId = mConfiguredOutputs.keyAt(i);
- Surface s = mConfiguredOutputs.valueAt(i);
+ OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
- if (!outputs.contains(s)) {
+ if (!outputs.contains(outConfig)) {
deleteList.add(streamId);
} else {
- addSet.remove(s); // Don't create a stream previously created
+ addSet.remove(outConfig); // Don't create a stream previously created
}
}
@@ -373,9 +380,11 @@ public class CameraDeviceImpl extends CameraDevice {
}
// Add all new streams
- for (Surface s : addSet) {
- int streamId = mRemoteDevice.createStream(s);
- mConfiguredOutputs.put(streamId, s);
+ for (OutputConfiguration outConfig : outputs) {
+ if (addSet.contains(outConfig)) {
+ int streamId = mRemoteDevice.createStream(outConfig);
+ mConfiguredOutputs.put(streamId, outConfig);
+ }
}
try {
@@ -444,12 +453,9 @@ public class CameraDeviceImpl extends CameraDevice {
// TODO: dont block for this
boolean configureSuccess = true;
CameraAccessException pendingException = null;
- List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
- for (OutputConfiguration config : outputConfigurations) {
- outSurfaces.add(config.getSurface());
- }
try {
- configureSuccess = configureOutputsChecked(outSurfaces); // and then block until IDLE
+ // configure outputs and then block until IDLE
+ configureSuccess = configureOutputsChecked(outputConfigurations);
} catch (CameraAccessException e) {
configureSuccess = false;
pendingException = e;
@@ -458,6 +464,10 @@ public class CameraDeviceImpl extends CameraDevice {
}
}
+ List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
+ for (OutputConfiguration config : outputConfigurations) {
+ outSurfaces.add(config.getSurface());
+ }
// Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
CameraCaptureSessionImpl newSession =
new CameraCaptureSessionImpl(mNextSessionId++,
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 26cd498..70f3463 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -26,6 +26,7 @@ import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.utils.LongParcelable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.os.ConditionVariable;
@@ -504,7 +505,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
- public int createStream(Surface surface) {
+ public int createStream(OutputConfiguration outputConfiguration) {
if (DEBUG) {
Log.d(TAG, "createStream called.");
}
@@ -518,8 +519,12 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
return CameraBinderDecorator.INVALID_OPERATION;
}
+ if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) {
+ Log.e(TAG, "Cannot create stream, stream rotation is not supported.");
+ return CameraBinderDecorator.INVALID_OPERATION;
+ }
int id = ++mSurfaceIdCounter;
- mSurfaces.put(id, surface);
+ mSurfaces.put(id, outputConfiguration.getSurface());
return id;
}
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 367a078..b5a019d 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -292,6 +292,10 @@ public class LegacyCameraDevice implements AutoCloseable {
Log.e(TAG, "configureOutputs - null outputs are not allowed");
return BAD_VALUE;
}
+ if (!output.isValid()) {
+ Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
+ return BAD_VALUE;
+ }
StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
@@ -522,7 +526,7 @@ public class LegacyCameraDevice implements AutoCloseable {
* @return the width and height of the surface
*
* @throws NullPointerException if the {@code surface} was {@code null}
- * @throws IllegalStateException if the {@code surface} was invalid
+ * @throws BufferQueueAbandonedException if the {@code surface} was invalid
*/
public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
checkNotNull(surface);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
index 7e0c01b..4b7cfbf 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
@@ -60,7 +60,7 @@ public class LegacyExceptionUtils {
case CameraBinderDecorator.NO_ERROR: {
return CameraBinderDecorator.NO_ERROR;
}
- case CameraBinderDecorator.ENODEV: {
+ case CameraBinderDecorator.BAD_VALUE: {
throw new BufferQueueAbandonedException();
}
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index ff74c59..691798f 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -498,6 +498,10 @@ public class RequestThreadManager {
return;
}
for(Surface s : surfaces) {
+ if (s == null || !s.isValid()) {
+ Log.w(TAG, "Jpeg surface is invalid, skipping...");
+ continue;
+ }
try {
LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
diff --git a/core/java/android/hardware/IProCameraCallbacks.aidl b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
index a09b452..0921cd8 100644
--- a/core/java/android/hardware/IProCameraCallbacks.aidl
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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.
@@ -14,13 +14,7 @@
* limitations under the License.
*/
-package android.hardware;
+package android.hardware.camera2.params;
/** @hide */
-interface IProCameraCallbacks
-{
- /**
- * Keep up-to-date with frameworks/av/include/camera/IProCameraCallbacks.h
- */
- // TODO: consider implementing this.
-}
+parcelable OutputConfiguration;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 47c784e..0a4ed39 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -18,19 +18,22 @@
package android.hardware.camera2.params;
import android.hardware.camera2.CameraDevice;
+import android.util.Log;
import android.view.Surface;
+import android.os.Parcel;
+import android.os.Parcelable;
import static com.android.internal.util.Preconditions.*;
/**
- * Immutable class for describing camera output, which contains a {@link Surface} and its specific
+ * A class for describing camera output, which contains a {@link Surface} and its specific
* configuration for creating capture session.
*
* @see CameraDevice#createCaptureSession
*
* @hide
*/
-public final class OutputConfiguration {
+public final class OutputConfiguration implements Parcelable {
/**
* Rotation constant: 0 degree rotation (no rotation)
@@ -93,6 +96,18 @@ public final class OutputConfiguration {
}
/**
+ * Create an OutputConfiguration from Parcel.
+ */
+ private OutputConfiguration(Parcel source) {
+ int rotation = source.readInt();
+ Surface surface = Surface.CREATOR.createFromParcel(source);
+ checkNotNull(surface, "Surface must not be null");
+ checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+ mSurface = surface;
+ mRotation = rotation;
+ }
+
+ /**
* Get the {@link Surface} associated with this {@link OutputConfiguration}.
*
* @return the {@link Surface} associated with this {@link OutputConfiguration}.
@@ -111,6 +126,40 @@ public final class OutputConfiguration {
return mRotation;
}
+ public static final Parcelable.Creator<OutputConfiguration> CREATOR =
+ new Parcelable.Creator<OutputConfiguration>() {
+ @Override
+ public OutputConfiguration createFromParcel(Parcel source) {
+ try {
+ OutputConfiguration outputConfiguration = new OutputConfiguration(source);
+ return outputConfiguration;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public OutputConfiguration[] newArray(int size) {
+ return new OutputConfiguration[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (dest == null) {
+ throw new IllegalArgumentException("dest must not be null");
+ }
+ dest.writeInt(mRotation);
+ mSurface.writeToParcel(dest, flags);
+ }
+
+ private static final String TAG = "OutputConfiguration";
private final Surface mSurface;
private final int mRotation;
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b077e06..12e1963 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -472,7 +472,8 @@ public final class DisplayManager {
/**
* Creates a virtual display.
*
- * @see #createVirtualDisplay(String, int, int, int, Surface, int, VirtualDisplay.Callback)
+ * @see #createVirtualDisplay(String, int, int, int, Surface, int,
+ * VirtualDisplay.Callback, Handler)
*/
public VirtualDisplay createVirtualDisplay(@NonNull String name,
int width, int height, int densityDpi, @Nullable Surface surface, int flags) {
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index 263d6b1..874b0c6 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -32,6 +32,9 @@ import android.util.Log;
public final class HdmiPlaybackClient extends HdmiClient {
private static final String TAG = "HdmiPlaybackClient";
+ // Logical address of TV. The secondary TV is not handled.
+ private static final int ADDR_TV = 0;
+
/**
* Listener used by the client to get the result of one touch play operation.
*/
@@ -103,6 +106,17 @@ public final class HdmiPlaybackClient extends HdmiClient {
}
}
+ /**
+ * Sends a &lt;Standby&gt; command to TV.
+ */
+ public void sendStandby() {
+ try {
+ mService.sendStandby(getDeviceType(), HdmiDeviceInfo.idForCecDevice(ADDR_TV));
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendStandby threw exception ", e);
+ }
+ }
+
private IHdmiControlCallback getCallbackWrapper(final OneTouchPlayCallback callback) {
return new IHdmiControlCallback.Stub() {
@Override
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index cef17dd..a94c1da 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -211,6 +211,19 @@ public final class HdmiTvClient extends HdmiClient {
}
}
+ /**
+ * Sends a &lt;Standby&gt; command to other device.
+ *
+ * @param deviceId device id to send the command to
+ */
+ public void sendStandby(int deviceId) {
+ try {
+ mService.sendStandby(getDeviceType(), deviceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendStandby threw exception ", e);
+ }
+ }
+
private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) {
return new IHdmiRecordListener.Stub() {
@Override
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 6e5d064..4696b2a 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -436,7 +436,7 @@ public final class GeofenceHardwareImpl {
int monitoringType,
int sourcesUsed) {
if(location == null) {
- Log.e(TAG, String.format("Invalid Geofence Transition: location=%p", location));
+ Log.e(TAG, String.format("Invalid Geofence Transition: location=null"));
return;
}
if(DEBUG) {
diff --git a/core/java/android/net/BaseDhcpStateMachine.java b/core/java/android/net/BaseDhcpStateMachine.java
new file mode 100644
index 0000000..a25847d
--- /dev/null
+++ b/core/java/android/net/BaseDhcpStateMachine.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import com.android.internal.util.StateMachine;
+
+/**
+ * Interface that must be implemented by DHCP state machines.
+ *
+ * This is an abstract class instead of a Java interface so that callers can just declare an object
+ * of this type and be able to call all the methods defined by either StateMachine or this class.
+ *
+ * @hide
+ */
+public abstract class BaseDhcpStateMachine extends StateMachine {
+ protected BaseDhcpStateMachine(String tag) {
+ super(tag);
+ }
+ public abstract void registerForPreDhcpNotification();
+ public abstract void doQuit();
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 34a0727..a0e2bf8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2337,7 +2337,7 @@ public class ConnectivityManager {
* successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
* <p>
- * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or
+ * Note that if you intend to invoke {@link #setProcessDefaultNetwork} or
* {@link Network#openConnection(java.net.URL)} then you must get a
* ConnectivityManager instance before doing so.
*/
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 5151a04..1b8adc8 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -47,7 +47,7 @@ import android.util.Log;
*
* @hide
*/
-public class DhcpStateMachine extends StateMachine {
+public class DhcpStateMachine extends BaseDhcpStateMachine {
private static final String TAG = "DhcpStateMachine";
private static final boolean DBG = false;
@@ -161,6 +161,7 @@ public class DhcpStateMachine extends StateMachine {
* This is used by Wifi at this time for the purpose of doing BT-Wifi coex
* handling during Dhcp
*/
+ @Override
public void registerForPreDhcpNotification() {
mRegisteredForPreDhcpNotification = true;
}
@@ -170,6 +171,7 @@ public class DhcpStateMachine extends StateMachine {
*
* @hide
*/
+ @Override
public void doQuit() {
quit();
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 1129c9e..7e92de2 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -54,7 +54,8 @@ interface INetworkPolicyManager {
void setRestrictBackground(boolean restrictBackground);
boolean getRestrictBackground();
+ void setDeviceIdleMode(boolean enabled);
+
NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
boolean isNetworkMetered(in NetworkState state);
-
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 2c3881c..6436e42 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -27,6 +27,14 @@ interface INetworkStatsService {
/** Start a statistics query session. */
INetworkStatsSession openSession();
+ /** Start a statistics query session. If calling package is profile or device owner then it is
+ * granted automatic access if apiLevel is NetworkStatsManager.API_LEVEL_DPC_ALLOWED. If
+ * apiLevel is at least NetworkStatsManager.API_LEVEL_REQUIRES_PACKAGE_USAGE_STATS then
+ * PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted
+ * READ_NETWORK_USAGE_STATS is checked for.
+ */
+ INetworkStatsSession openSessionForUsageStats(String callingPackage);
+
/** Return network layer usage total for traffic that matches template. */
long getNetworkTotalBytes(in NetworkTemplate template, long start, long end);
diff --git a/core/java/android/net/INetworkStatsSession.aidl b/core/java/android/net/INetworkStatsSession.aidl
index 1596fa2..7bcb043 100644
--- a/core/java/android/net/INetworkStatsSession.aidl
+++ b/core/java/android/net/INetworkStatsSession.aidl
@@ -23,6 +23,9 @@ import android.net.NetworkTemplate;
/** {@hide} */
interface INetworkStatsSession {
+ /** Return device aggregated network layer usage summary for traffic that matches template. */
+ NetworkStats getDeviceSummaryForNetwork(in NetworkTemplate template, long start, long end);
+
/** Return network layer usage summary for traffic that matches template. */
NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return historical network layer stats for traffic that matches template. */
@@ -33,6 +36,9 @@ interface INetworkStatsSession {
/** Return historical network layer stats for specific UID traffic that matches template. */
NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
+ /** Return array of uids that have stats and are accessible to the calling user */
+ int[] getRelevantUids();
+
void close();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a7f9c5b..8c8bfab 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -148,9 +148,9 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int NET_CAPABILITY_TRUSTED = 14;
- /*
+ /**
* Indicates that this network is not a VPN. This capability is set by default and should be
- * explicitly cleared when creating VPN networks.
+ * explicitly cleared for VPN networks.
*/
public static final int NET_CAPABILITY_NOT_VPN = 15;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 8003afb..02fbe73 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,9 +16,11 @@
package android.net;
+import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Locale;
@@ -139,6 +141,11 @@ public class NetworkUtils {
public native static String getDhcpError();
/**
+ * Attaches a socket filter that accepts DHCP packets to the given socket.
+ */
+ public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
+
+ /**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
@@ -171,6 +178,15 @@ public class NetworkUtils {
public native static int bindSocketToNetwork(int socketfd, int netId);
/**
+ * Protect {@code fd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public static boolean protectFromVpn(FileDescriptor fd) {
+ return protectFromVpn(fd.getInt$());
+ }
+
+ /**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
* this socket will go directly to the underlying network, so its traffic will not be
* forwarded through the VPN.
@@ -230,6 +246,25 @@ public class NetworkUtils {
}
/**
+ * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
+ * @param netmask as a {@code Inet4Address}.
+ * @return the network prefix length
+ * @throws IllegalArgumentException the specified netmask was not contiguous.
+ * @hide
+ */
+ public static int netmaskToPrefixLength(Inet4Address netmask) {
+ // inetAddressToInt returns an int in *network* byte order.
+ int i = Integer.reverseBytes(inetAddressToInt(netmask));
+ int prefixLength = Integer.bitCount(i);
+ int trailingZeros = Integer.numberOfTrailingZeros(i);
+ if (trailingZeros != 32 - prefixLength) {
+ throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
+ }
+ return prefixLength;
+ }
+
+
+ /**
* Create an InetAddress from a string where the string must be a standard
* representation of a V4 or V6 address. Avoids doing a DNS lookup on failure
* but it will throw an IllegalArgumentException in that case.
@@ -309,6 +344,22 @@ public class NetworkUtils {
}
/**
+ * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
+ */
+ public static int getImplicitNetmask(Inet4Address address) {
+ int firstByte = address.getAddress()[0] & 0xff; // Convert to an unsigned value.
+ if (firstByte < 128) {
+ return 8;
+ } else if (firstByte < 192) {
+ return 16;
+ } else if (firstByte < 224) {
+ return 24;
+ } else {
+ return 32; // Will likely not end well for other reasons.
+ }
+ }
+
+ /**
* Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
* @hide
*/
diff --git a/core/java/android/net/dhcp/DhcpAckPacket.java b/core/java/android/net/dhcp/DhcpAckPacket.java
deleted file mode 100644
index 7b8be9c..0000000
--- a/core/java/android/net/dhcp/DhcpAckPacket.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-ACK packet.
- */
-class DhcpAckPacket extends DhcpPacket {
-
- /**
- * The address of the server which sent this packet.
- */
- private final InetAddress mSrcIp;
-
- DhcpAckPacket(int transId, boolean broadcast, InetAddress serverAddress,
- InetAddress clientIp, byte[] clientMac) {
- super(transId, Inet4Address.ANY, clientIp, serverAddress,
- Inet4Address.ANY, clientMac, broadcast);
- mBroadcast = broadcast;
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = " DNS servers: ";
-
- for (InetAddress dnsServer: mDnsServers) {
- dnsServers += dnsServer.toString() + " ";
- }
-
- return s + " ACK: your new IP " + mYourIp +
- ", netmask " + mSubnetMask +
- ", gateway " + mGateway + dnsServers +
- ", lease time " + mLeaseTime;
- }
-
- /**
- * Fills in a packet with the requested ACK parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
- InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated ACK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_LEASE_TIME, mLeaseTime);
-
- // the client should renew at 1/2 the lease-expiry interval
- if (mLeaseTime != null) {
- addTlv(buffer, DHCP_RENEWAL_TIME,
- Integer.valueOf(mLeaseTime.intValue() / 2));
- }
-
- addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buffer, DHCP_ROUTER, mGateway);
- addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
- addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
- addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
- addTlvEnd(buffer);
- }
-
- /**
- * Un-boxes an Integer, returning 0 if a null reference is supplied.
- */
- private static final int getInt(Integer v) {
- if (v == null) {
- return 0;
- } else {
- return v.intValue();
- }
- }
-
- /**
- * Notifies the specified state machine of the ACK packet parameters.
- */
- public void doNextOp(DhcpStateMachine machine) {
- machine.onAckReceived(mYourIp, mSubnetMask, mGateway, mDnsServers,
- mServerIdentifier, getInt(mLeaseTime));
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpDeclinePacket.java b/core/java/android/net/dhcp/DhcpDeclinePacket.java
deleted file mode 100644
index 7646eb4..0000000
--- a/core/java/android/net/dhcp/DhcpDeclinePacket.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DECLINE packet.
- */
-class DhcpDeclinePacket extends DhcpPacket {
- /**
- * Generates a DECLINE packet with the specified parameters.
- */
- DhcpDeclinePacket(int transId, InetAddress clientIp, InetAddress yourIp,
- InetAddress nextIp, InetAddress relayIp,
- byte[] clientMac) {
- super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DECLINE";
- }
-
- /**
- * Fills in a packet with the requested DECLINE attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to the DECLINE packet.
- */
- void finishPacket(ByteBuffer buffer) {
- // None needed
- }
-
- /**
- * Informs the state machine of the arrival of a DECLINE packet.
- */
- public void doNextOp(DhcpStateMachine machine) {
- machine.onDeclineReceived(mClientMac, mRequestedIp);
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpDiscoverPacket.java b/core/java/android/net/dhcp/DhcpDiscoverPacket.java
deleted file mode 100644
index 0e2d39b..0000000
--- a/core/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DISCOVER packet.
- */
-class DhcpDiscoverPacket extends DhcpPacket {
- /**
- * Generates a DISCOVER packet with the specified parameters.
- */
- DhcpDiscoverPacket(int transId, byte[] clientMac, boolean broadcast) {
- super(transId, Inet4Address.ANY, Inet4Address.ANY, Inet4Address.ANY,
- Inet4Address.ANY, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DISCOVER " +
- (mBroadcast ? "broadcast " : "unicast ");
- }
-
- /**
- * Fills in a packet with the requested DISCOVER parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- InetAddress destIp = Inet4Address.ALL;
-
- fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
- result, DHCP_BOOTREQUEST, true);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to a DISCOVER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-
- /**
- * Informs the state machine of the arrival of a DISCOVER packet.
- */
- public void doNextOp(DhcpStateMachine machine) {
- // currently omitted: host name
- machine.onDiscoverReceived(mBroadcast, mTransId, mClientMac,
- mRequestedParams);
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpInformPacket.java b/core/java/android/net/dhcp/DhcpInformPacket.java
deleted file mode 100644
index da73216..0000000
--- a/core/java/android/net/dhcp/DhcpInformPacket.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the (unused) DHCP-INFORM packet.
- */
-class DhcpInformPacket extends DhcpPacket {
- /**
- * Generates an INFORM packet with the specified parameters.
- */
- DhcpInformPacket(int transId, InetAddress clientIp, InetAddress yourIp,
- InetAddress nextIp, InetAddress relayIp,
- byte[] clientMac) {
- super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " INFORM";
- }
-
- /**
- * Builds an INFORM packet.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds additional parameters to the INFORM packet.
- */
- void finishPacket(ByteBuffer buffer) {
- byte[] clientId = new byte[7];
-
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, 6);
-
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-
- /**
- * Informs the state machine of the arrival of an INFORM packet. Not
- * used currently.
- */
- public void doNextOp(DhcpStateMachine machine) {
- InetAddress clientRequest =
- mRequestedIp == null ? mClientIp : mRequestedIp;
- machine.onInformReceived(mTransId, mClientMac, clientRequest,
- mRequestedParams);
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpNakPacket.java b/core/java/android/net/dhcp/DhcpNakPacket.java
deleted file mode 100644
index 1f340ad..0000000
--- a/core/java/android/net/dhcp/DhcpNakPacket.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-NAK packet.
- */
-class DhcpNakPacket extends DhcpPacket {
- /**
- * Generates a NAK packet with the specified parameters.
- */
- DhcpNakPacket(int transId, InetAddress clientIp, InetAddress yourIp,
- InetAddress nextIp, InetAddress relayIp,
- byte[] clientMac) {
- super(transId, Inet4Address.ANY, Inet4Address.ANY, nextIp, relayIp,
- clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage);
- }
-
- /**
- * Fills in a packet with the requested NAK attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- InetAddress destIp = mClientIp;
- InetAddress srcIp = mYourIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated NAK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_MESSAGE, mMessage);
- addTlvEnd(buffer);
- }
-
- /**
- * Notifies the specified state machine of the newly-arrived NAK packet.
- */
- public void doNextOp(DhcpStateMachine machine) {
- machine.onNakReceived();
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpOfferPacket.java b/core/java/android/net/dhcp/DhcpOfferPacket.java
deleted file mode 100644
index f1c30e1..0000000
--- a/core/java/android/net/dhcp/DhcpOfferPacket.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-OFFER packet.
- */
-class DhcpOfferPacket extends DhcpPacket {
- /**
- * The IP address of the server which sent this packet.
- */
- private final InetAddress mSrcIp;
-
- /**
- * Generates a OFFER packet with the specified parameters.
- */
- DhcpOfferPacket(int transId, boolean broadcast, InetAddress serverAddress,
- InetAddress clientIp, byte[] clientMac) {
- super(transId, Inet4Address.ANY, clientIp, Inet4Address.ANY,
- Inet4Address.ANY, clientMac, broadcast);
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = ", DNS servers: ";
-
- if (mDnsServers != null) {
- for (InetAddress dnsServer: mDnsServers) {
- dnsServers += dnsServer + " ";
- }
- }
-
- return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateway " + mGateway +
- " lease time " + mLeaseTime + ", domain " + mDomainName;
- }
-
- /**
- * Fills in a packet with the specified OFFER attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
- InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the server-generated OFFER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_LEASE_TIME, mLeaseTime);
-
- // the client should renew at 1/2 the lease-expiry interval
- if (mLeaseTime != null) {
- addTlv(buffer, DHCP_RENEWAL_TIME,
- Integer.valueOf(mLeaseTime.intValue() / 2));
- }
-
- addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buffer, DHCP_ROUTER, mGateway);
- addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
- addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
- addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
- addTlvEnd(buffer);
- }
-
- /**
- * Notifies the state machine of the OFFER packet parameters.
- */
- public void doNextOp(DhcpStateMachine machine) {
- machine.onOfferReceived(mBroadcast, mTransId, mClientMac, mYourIp,
- mServerIdentifier);
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpPacket.java b/core/java/android/net/dhcp/DhcpPacket.java
deleted file mode 100644
index c7c25f0..0000000
--- a/core/java/android/net/dhcp/DhcpPacket.java
+++ /dev/null
@@ -1,895 +0,0 @@
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.nio.ShortBuffer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Defines basic data and operations needed to build and use packets for the
- * DHCP protocol. Subclasses create the specific packets used at each
- * stage of the negotiation.
- */
-abstract class DhcpPacket {
- protected static final String TAG = "DhcpPacket";
-
- /**
- * Packet encapsulations.
- */
- public static final int ENCAP_L2 = 0; // EthernetII header included
- public static final int ENCAP_L3 = 1; // IP/UDP header included
- public static final int ENCAP_BOOTP = 2; // BOOTP contents only
-
- /**
- * IP layer definitions.
- */
- private static final byte IP_TYPE_UDP = (byte) 0x11;
-
- /**
- * IP: Version 4, Header Length 20 bytes
- */
- private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
-
- /**
- * IP: Flags 0, Fragment Offset 0, Don't Fragment
- */
- private static final short IP_FLAGS_OFFSET = (short) 0x4000;
-
- /**
- * IP: TOS
- */
- private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
-
- /**
- * IP: TTL -- use default 64 from RFC1340
- */
- private static final byte IP_TTL = (byte) 0x40;
-
- /**
- * The client DHCP port.
- */
- static final short DHCP_CLIENT = (short) 68;
-
- /**
- * The server DHCP port.
- */
- static final short DHCP_SERVER = (short) 67;
-
- /**
- * The message op code indicating a request from a client.
- */
- protected static final byte DHCP_BOOTREQUEST = (byte) 1;
-
- /**
- * The message op code indicating a response from the server.
- */
- protected static final byte DHCP_BOOTREPLY = (byte) 2;
-
- /**
- * The code type used to identify an Ethernet MAC address in the
- * Client-ID field.
- */
- protected static final byte CLIENT_ID_ETHER = (byte) 1;
-
- /**
- * The maximum length of a packet that can be constructed.
- */
- protected static final int MAX_LENGTH = 1500;
-
- /**
- * DHCP Optional Type: DHCP Subnet Mask
- */
- protected static final byte DHCP_SUBNET_MASK = 1;
- protected InetAddress mSubnetMask;
-
- /**
- * DHCP Optional Type: DHCP Router
- */
- protected static final byte DHCP_ROUTER = 3;
- protected InetAddress mGateway;
-
- /**
- * DHCP Optional Type: DHCP DNS Server
- */
- protected static final byte DHCP_DNS_SERVER = 6;
- protected List<InetAddress> mDnsServers;
-
- /**
- * DHCP Optional Type: DHCP Host Name
- */
- protected static final byte DHCP_HOST_NAME = 12;
- protected String mHostName;
-
- /**
- * DHCP Optional Type: DHCP DOMAIN NAME
- */
- protected static final byte DHCP_DOMAIN_NAME = 15;
- protected String mDomainName;
-
- /**
- * DHCP Optional Type: DHCP BROADCAST ADDRESS
- */
- protected static final byte DHCP_BROADCAST_ADDRESS = 28;
- protected InetAddress mBroadcastAddress;
-
- /**
- * DHCP Optional Type: DHCP Requested IP Address
- */
- protected static final byte DHCP_REQUESTED_IP = 50;
- protected InetAddress mRequestedIp;
-
- /**
- * DHCP Optional Type: DHCP Lease Time
- */
- protected static final byte DHCP_LEASE_TIME = 51;
- protected Integer mLeaseTime;
-
- /**
- * DHCP Optional Type: DHCP Message Type
- */
- protected static final byte DHCP_MESSAGE_TYPE = 53;
- // the actual type values
- protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
- protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
- protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
- protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
- protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
- protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
- protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
-
- /**
- * DHCP Optional Type: DHCP Server Identifier
- */
- protected static final byte DHCP_SERVER_IDENTIFIER = 54;
- protected InetAddress mServerIdentifier;
-
- /**
- * DHCP Optional Type: DHCP Parameter List
- */
- protected static final byte DHCP_PARAMETER_LIST = 55;
- protected byte[] mRequestedParams;
-
- /**
- * DHCP Optional Type: DHCP MESSAGE
- */
- protected static final byte DHCP_MESSAGE = 56;
- protected String mMessage;
-
- /**
- * DHCP Optional Type: DHCP Renewal Time Value
- */
- protected static final byte DHCP_RENEWAL_TIME = 58;
-
- /**
- * DHCP Optional Type: Vendor Class Identifier
- */
- protected static final byte DHCP_VENDOR_CLASS_ID = 60;
-
- /**
- * DHCP Optional Type: DHCP Client Identifier
- */
- protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
-
- /**
- * The transaction identifier used in this particular DHCP negotiation
- */
- protected final int mTransId;
-
- /**
- * The IP address of the client host. This address is typically
- * proposed by the client (from an earlier DHCP negotiation) or
- * supplied by the server.
- */
- protected final InetAddress mClientIp;
- protected final InetAddress mYourIp;
- private final InetAddress mNextIp;
- private final InetAddress mRelayIp;
-
- /**
- * Does the client request a broadcast response?
- */
- protected boolean mBroadcast;
-
- /**
- * The six-octet MAC of the client.
- */
- protected final byte[] mClientMac;
-
- /**
- * Asks the packet object to signal the next operation in the DHCP
- * protocol. The available actions are methods defined in the
- * DhcpStateMachine interface.
- */
- public abstract void doNextOp(DhcpStateMachine stateMachine);
-
- /**
- * Asks the packet object to create a ByteBuffer serialization of
- * the packet for transmission.
- */
- public abstract ByteBuffer buildPacket(int encap, short destUdp,
- short srcUdp);
-
- /**
- * Allows the concrete class to fill in packet-type-specific details,
- * typically optional parameters at the end of the packet.
- */
- abstract void finishPacket(ByteBuffer buffer);
-
- protected DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp,
- InetAddress nextIp, InetAddress relayIp,
- byte[] clientMac, boolean broadcast) {
- mTransId = transId;
- mClientIp = clientIp;
- mYourIp = yourIp;
- mNextIp = nextIp;
- mRelayIp = relayIp;
- mClientMac = clientMac;
- mBroadcast = broadcast;
- }
-
- /**
- * Returns the transaction ID.
- */
- public int getTransactionId() {
- return mTransId;
- }
-
- /**
- * Creates a new L3 packet (including IP header) containing the
- * DHCP udp packet. This method relies upon the delegated method
- * finishPacket() to insert the per-packet contents.
- */
- protected void fillInPacket(int encap, InetAddress destIp,
- InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf,
- byte requestCode, boolean broadcast) {
- byte[] destIpArray = destIp.getAddress();
- byte[] srcIpArray = srcIp.getAddress();
- int ipLengthOffset = 0;
- int ipChecksumOffset = 0;
- int endIpHeader = 0;
- int udpHeaderOffset = 0;
- int udpLengthOffset = 0;
- int udpChecksumOffset = 0;
-
- buf.clear();
- buf.order(ByteOrder.BIG_ENDIAN);
-
- // if a full IP packet needs to be generated, put the IP & UDP
- // headers in place, and pre-populate with artificial values
- // needed to seed the IP checksum.
- if (encap == ENCAP_L3) {
- // fake IP header, used in the IP-header checksum
- buf.put(IP_VERSION_HEADER_LEN);
- buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY
- ipLengthOffset = buf.position();
- buf.putShort((short)0); // length
- buf.putShort((short)0); // id
- buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
- buf.put(IP_TTL); // TTL: use default 64 from RFC1340
- buf.put(IP_TYPE_UDP);
- ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // checksum
-
- buf.put(srcIpArray);
- buf.put(destIpArray);
- endIpHeader = buf.position();
-
- // UDP header
- udpHeaderOffset = buf.position();
- buf.putShort(srcUdp);
- buf.putShort(destUdp);
- udpLengthOffset = buf.position();
- buf.putShort((short) 0); // length
- udpChecksumOffset = buf.position();
- buf.putShort((short) 0); // UDP checksum -- initially zero
- }
-
- // DHCP payload
- buf.put(requestCode);
- buf.put((byte) 1); // Hardware Type: Ethernet
- buf.put((byte) mClientMac.length); // Hardware Address Length
- buf.put((byte) 0); // Hop Count
- buf.putInt(mTransId); // Transaction ID
- buf.putShort((short) 0); // Elapsed Seconds
-
- if (broadcast) {
- buf.putShort((short) 0x8000); // Flags
- } else {
- buf.putShort((short) 0x0000); // Flags
- }
-
- buf.put(mClientIp.getAddress());
- buf.put(mYourIp.getAddress());
- buf.put(mNextIp.getAddress());
- buf.put(mRelayIp.getAddress());
- buf.put(mClientMac);
- buf.position(buf.position() +
- (16 - mClientMac.length) // pad addr to 16 bytes
- + 64 // empty server host name (64 bytes)
- + 128); // empty boot file name (128 bytes)
- buf.putInt(0x63825363); // magic number
- finishPacket(buf);
-
- // round up to an even number of octets
- if ((buf.position() & 1) == 1) {
- buf.put((byte) 0);
- }
-
- // If an IP packet is being built, the IP & UDP checksums must be
- // computed.
- if (encap == ENCAP_L3) {
- // fix UDP header: insert length
- short udpLen = (short)(buf.position() - udpHeaderOffset);
- buf.putShort(udpLengthOffset, udpLen);
- // fix UDP header: checksum
- // checksum for UDP at udpChecksumOffset
- int udpSeed = 0;
-
- // apply IPv4 pseudo-header. Read IP address src and destination
- // values from the IP header and accumulate checksum.
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
-
- // accumulate extra data for the pseudo-header
- udpSeed += IP_TYPE_UDP;
- udpSeed += udpLen;
- // and compute UDP checksum
- buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
- udpHeaderOffset,
- buf.position()));
- // fix IP header: insert length
- buf.putShort(ipLengthOffset, (short)buf.position());
- // fixup IP-header checksum
- buf.putShort(ipChecksumOffset,
- (short) checksum(buf, 0, 0, endIpHeader));
- }
- }
-
- /**
- * Converts a signed short value to an unsigned int value. Needed
- * because Java does not have unsigned types.
- */
- private int intAbs(short v) {
- if (v < 0) {
- int r = v + 65536;
- return r;
- } else {
- return(v);
- }
- }
-
- /**
- * Performs an IP checksum (used in IP header and across UDP
- * payload) on the specified portion of a ByteBuffer. The seed
- * allows the checksum to commence with a specified value.
- */
- private int checksum(ByteBuffer buf, int seed, int start, int end) {
- int sum = seed;
- int bufPosition = buf.position();
-
- // set position of original ByteBuffer, so that the ShortBuffer
- // will be correctly initialized
- buf.position(start);
- ShortBuffer shortBuf = buf.asShortBuffer();
-
- // re-set ByteBuffer position
- buf.position(bufPosition);
-
- short[] shortArray = new short[(end - start) / 2];
- shortBuf.get(shortArray);
-
- for (short s : shortArray) {
- sum += intAbs(s);
- }
-
- start += shortArray.length * 2;
-
- // see if a singleton byte remains
- if (end != start) {
- short b = buf.get(start);
-
- // make it unsigned
- if (b < 0) {
- b += 256;
- }
-
- sum += b * 256;
- }
-
- sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
- sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
- int negated = ~sum;
- return intAbs((short) negated);
- }
-
- /**
- * Adds an optional parameter containing a single byte value.
- */
- protected void addTlv(ByteBuffer buf, byte type, byte value) {
- buf.put(type);
- buf.put((byte) 1);
- buf.put(value);
- }
-
- /**
- * Adds an optional parameter containing an array of bytes.
- */
- protected void addTlv(ByteBuffer buf, byte type, byte[] payload) {
- if (payload != null) {
- buf.put(type);
- buf.put((byte) payload.length);
- buf.put(payload);
- }
- }
-
- /**
- * Adds an optional parameter containing an IP address.
- */
- protected void addTlv(ByteBuffer buf, byte type, InetAddress addr) {
- if (addr != null) {
- addTlv(buf, type, addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a list of IP addresses.
- */
- protected void addTlv(ByteBuffer buf, byte type, List<InetAddress> addrs) {
- if (addrs != null && addrs.size() > 0) {
- buf.put(type);
- buf.put((byte)(4 * addrs.size()));
-
- for (InetAddress addr : addrs) {
- buf.put(addr.getAddress());
- }
- }
- }
-
- /**
- * Adds an optional parameter containing a simple integer
- */
- protected void addTlv(ByteBuffer buf, byte type, Integer value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 4);
- buf.putInt(value.intValue());
- }
- }
-
- /**
- * Adds an optional parameter containing and ASCII string.
- */
- protected void addTlv(ByteBuffer buf, byte type, String str) {
- if (str != null) {
- buf.put(type);
- buf.put((byte) str.length());
-
- for (int i = 0; i < str.length(); i++) {
- buf.put((byte) str.charAt(i));
- }
- }
- }
-
- /**
- * Adds the special end-of-optional-parameters indicator.
- */
- protected void addTlvEnd(ByteBuffer buf) {
- buf.put((byte) 0xFF);
- }
-
- /**
- * Converts a MAC from an array of octets to an ASCII string.
- */
- public static String macToString(byte[] mac) {
- String macAddr = "";
-
- for (int i = 0; i < mac.length; i++) {
- String hexString = "0" + Integer.toHexString(mac[i]);
-
- // substring operation grabs the last 2 digits: this
- // allows signed bytes to be converted correctly.
- macAddr += hexString.substring(hexString.length() - 2);
-
- if (i != (mac.length - 1)) {
- macAddr += ":";
- }
- }
-
- return macAddr;
- }
-
- public String toString() {
- String macAddr = macToString(mClientMac);
-
- return macAddr;
- }
-
- /**
- * Reads a four-octet value from a ByteBuffer and construct
- * an IPv4 address from that value.
- */
- private static InetAddress readIpAddress(ByteBuffer packet) {
- InetAddress result = null;
- byte[] ipAddr = new byte[4];
- packet.get(ipAddr);
-
- try {
- result = InetAddress.getByAddress(ipAddr);
- } catch (UnknownHostException ex) {
- // ipAddr is numeric, so this should not be
- // triggered. However, if it is, just nullify
- result = null;
- }
-
- return result;
- }
-
- /**
- * Reads a string of specified length from the buffer.
- */
- private static String readAsciiString(ByteBuffer buf, int byteCount) {
- byte[] bytes = new byte[byteCount];
- buf.get(bytes);
- return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
- }
-
- /**
- * Creates a concrete DhcpPacket from the supplied ByteBuffer. The
- * buffer may have an L2 encapsulation (which is the full EthernetII
- * format starting with the source-address MAC) or an L3 encapsulation
- * (which starts with the IP header).
- * <br>
- * A subset of the optional parameters are parsed and are stored
- * in object fields.
- */
- public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
- {
- // bootp parameters
- int transactionId;
- InetAddress clientIp;
- InetAddress yourIp;
- InetAddress nextIp;
- InetAddress relayIp;
- byte[] clientMac;
- List<InetAddress> dnsServers = new ArrayList<InetAddress>();
- InetAddress gateway = null; // aka router
- Integer leaseTime = null;
- InetAddress serverIdentifier = null;
- InetAddress netMask = null;
- String message = null;
- String vendorId = null;
- byte[] expectedParams = null;
- String hostName = null;
- String domainName = null;
- InetAddress ipSrc = null;
- InetAddress ipDst = null;
- InetAddress bcAddr = null;
- InetAddress requestedIp = null;
-
- // dhcp options
- byte dhcpType = (byte) 0xFF;
-
- packet.order(ByteOrder.BIG_ENDIAN);
-
- // check to see if we need to parse L2, IP, and UDP encaps
- if (pktType == ENCAP_L2) {
- // System.out.println("buffer len " + packet.limit());
- byte[] l2dst = new byte[6];
- byte[] l2src = new byte[6];
-
- packet.get(l2dst);
- packet.get(l2src);
-
- short l2type = packet.getShort();
-
- if (l2type != 0x0800)
- return null;
- }
-
- if ((pktType == ENCAP_L2) || (pktType == ENCAP_L3)) {
- // assume l2type is 0x0800, i.e. IP
- byte ipType = packet.get();
- // System.out.println("ipType is " + ipType);
- byte ipDiffServicesField = packet.get();
- short ipTotalLength = packet.getShort();
- short ipIdentification = packet.getShort();
- byte ipFlags = packet.get();
- byte ipFragOffset = packet.get();
- byte ipTTL = packet.get();
- byte ipProto = packet.get();
- short ipChksm = packet.getShort();
-
- ipSrc = readIpAddress(packet);
- ipDst = readIpAddress(packet);
-
- if (ipProto != IP_TYPE_UDP) // UDP
- return null;
-
- // assume UDP
- short udpSrcPort = packet.getShort();
- short udpDstPort = packet.getShort();
- short udpLen = packet.getShort();
- short udpChkSum = packet.getShort();
-
- if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
- return null;
- }
-
- // assume bootp
- byte type = packet.get();
- byte hwType = packet.get();
- byte addrLen = packet.get();
- byte hops = packet.get();
- transactionId = packet.getInt();
- short elapsed = packet.getShort();
- short bootpFlags = packet.getShort();
- boolean broadcast = (bootpFlags & 0x8000) != 0;
- byte[] ipv4addr = new byte[4];
-
- try {
- packet.get(ipv4addr);
- clientIp = InetAddress.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- yourIp = InetAddress.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- nextIp = InetAddress.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- relayIp = InetAddress.getByAddress(ipv4addr);
- } catch (UnknownHostException ex) {
- return null;
- }
-
- clientMac = new byte[addrLen];
- packet.get(clientMac);
-
- // skip over address padding (16 octets allocated)
- packet.position(packet.position() + (16 - addrLen)
- + 64 // skip server host name (64 chars)
- + 128); // skip boot file name (128 chars)
-
- int dhcpMagicCookie = packet.getInt();
-
- if (dhcpMagicCookie != 0x63825363)
- return null;
-
- // parse options
- boolean notFinishedOptions = true;
-
- while ((packet.position() < packet.limit()) && notFinishedOptions) {
- byte optionType = packet.get();
-
- if (optionType == (byte) 0xFF) {
- notFinishedOptions = false;
- } else {
- byte optionLen = packet.get();
- int expectedLen = 0;
-
- switch(optionType) {
- case DHCP_SUBNET_MASK:
- netMask = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_ROUTER:
- gateway = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_DNS_SERVER:
- expectedLen = 0;
-
- for (expectedLen = 0; expectedLen < optionLen;
- expectedLen += 4) {
- dnsServers.add(readIpAddress(packet));
- }
- break;
- case DHCP_HOST_NAME:
- expectedLen = optionLen;
- hostName = readAsciiString(packet, optionLen);
- break;
- case DHCP_DOMAIN_NAME:
- expectedLen = optionLen;
- domainName = readAsciiString(packet, optionLen);
- break;
- case DHCP_BROADCAST_ADDRESS:
- bcAddr = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_REQUESTED_IP:
- requestedIp = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_LEASE_TIME:
- leaseTime = Integer.valueOf(packet.getInt());
- expectedLen = 4;
- break;
- case DHCP_MESSAGE_TYPE:
- dhcpType = packet.get();
- expectedLen = 1;
- break;
- case DHCP_SERVER_IDENTIFIER:
- serverIdentifier = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_PARAMETER_LIST:
- expectedParams = new byte[optionLen];
- packet.get(expectedParams);
- expectedLen = optionLen;
- break;
- case DHCP_MESSAGE:
- expectedLen = optionLen;
- message = readAsciiString(packet, optionLen);
- break;
- case DHCP_VENDOR_CLASS_ID:
- expectedLen = optionLen;
- vendorId = readAsciiString(packet, optionLen);
- break;
- case DHCP_CLIENT_IDENTIFIER: { // Client identifier
- byte[] id = new byte[optionLen];
- packet.get(id);
- expectedLen = optionLen;
- } break;
- default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
- }
-
- if (expectedLen != optionLen) {
- return null;
- }
- }
- }
-
- DhcpPacket newPacket;
-
- switch(dhcpType) {
- case -1: return null;
- case DHCP_MESSAGE_TYPE_DISCOVER:
- newPacket = new DhcpDiscoverPacket(
- transactionId, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_OFFER:
- newPacket = new DhcpOfferPacket(
- transactionId, broadcast, ipSrc, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_REQUEST:
- newPacket = new DhcpRequestPacket(
- transactionId, clientIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_DECLINE:
- newPacket = new DhcpDeclinePacket(
- transactionId, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- case DHCP_MESSAGE_TYPE_ACK:
- newPacket = new DhcpAckPacket(
- transactionId, broadcast, ipSrc, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_NAK:
- newPacket = new DhcpNakPacket(
- transactionId, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- case DHCP_MESSAGE_TYPE_INFORM:
- newPacket = new DhcpInformPacket(
- transactionId, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- default:
- System.out.println("Unimplemented type: " + dhcpType);
- return null;
- }
-
- newPacket.mBroadcastAddress = bcAddr;
- newPacket.mDnsServers = dnsServers;
- newPacket.mDomainName = domainName;
- newPacket.mGateway = gateway;
- newPacket.mHostName = hostName;
- newPacket.mLeaseTime = leaseTime;
- newPacket.mMessage = message;
- newPacket.mRequestedIp = requestedIp;
- newPacket.mRequestedParams = expectedParams;
- newPacket.mServerIdentifier = serverIdentifier;
- newPacket.mSubnetMask = netMask;
- return newPacket;
- }
-
- /**
- * Parse a packet from an array of bytes.
- */
- public static DhcpPacket decodeFullPacket(byte[] packet, int pktType)
- {
- ByteBuffer buffer = ByteBuffer.wrap(packet).order(ByteOrder.BIG_ENDIAN);
- return decodeFullPacket(buffer, pktType);
- }
-
- /**
- * Builds a DHCP-DISCOVER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
- byte[] clientMac, boolean broadcast, byte[] expectedParams) {
- DhcpPacket pkt = new DhcpDiscoverPacket(
- transactionId, clientMac, broadcast);
- pkt.mRequestedParams = expectedParams;
- return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- }
-
- /**
- * Builds a DHCP-OFFER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildOfferPacket(int encap, int transactionId,
- boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
- byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
- InetAddress gateway, List<InetAddress> dnsServers,
- InetAddress dhcpServerIdentifier, String domainName) {
- DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
- pkt.mGateway = gateway;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mSubnetMask = netMask;
- pkt.mBroadcastAddress = bcAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-ACK packet from the required specified parameters.
- */
- public static ByteBuffer buildAckPacket(int encap, int transactionId,
- boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
- byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
- InetAddress gateway, List<InetAddress> dnsServers,
- InetAddress dhcpServerIdentifier, String domainName) {
- DhcpPacket pkt = new DhcpAckPacket(
- transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
- pkt.mGateway = gateway;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mSubnetMask = netMask;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mBroadcastAddress = bcAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-NAK packet from the required specified parameters.
- */
- public static ByteBuffer buildNakPacket(int encap, int transactionId,
- InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac) {
- DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
- serverIpAddr, serverIpAddr, serverIpAddr, mac);
- pkt.mMessage = "requested address not available";
- pkt.mRequestedIp = clientIpAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-REQUEST packet from the required specified parameters.
- */
- public static ByteBuffer buildRequestPacket(int encap,
- int transactionId, InetAddress clientIp, boolean broadcast,
- byte[] clientMac, InetAddress requestedIpAddress,
- InetAddress serverIdentifier, byte[] requestedParams, String hostName) {
- DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
- clientMac, broadcast);
- pkt.mRequestedIp = requestedIpAddress;
- pkt.mServerIdentifier = serverIdentifier;
- pkt.mHostName = hostName;
- pkt.mRequestedParams = requestedParams;
- ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- return result;
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpRequestPacket.java b/core/java/android/net/dhcp/DhcpRequestPacket.java
deleted file mode 100644
index cf32957..0000000
--- a/core/java/android/net/dhcp/DhcpRequestPacket.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import android.util.Log;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-REQUEST packet.
- */
-class DhcpRequestPacket extends DhcpPacket {
- /**
- * Generates a REQUEST packet with the specified parameters.
- */
- DhcpRequestPacket(int transId, InetAddress clientIp, byte[] clientMac,
- boolean broadcast) {
- super(transId, clientIp, Inet4Address.ANY, Inet4Address.ANY,
- Inet4Address.ANY, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " REQUEST, desired IP " + mRequestedIp + " from host '"
- + mHostName + "', param list length "
- + (mRequestedParams == null ? 0 : mRequestedParams.length);
- }
-
- /**
- * Fills in a packet with the requested REQUEST attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
- result, DHCP_BOOTREQUEST, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated REQUEST packet.
- */
- void finishPacket(ByteBuffer buffer) {
- byte[] clientId = new byte[7];
-
- // assemble client identifier
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, 6);
-
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, clientId);
- addTlvEnd(buffer);
- }
-
- /**
- * Notifies the specified state machine of the REQUEST packet parameters.
- */
- public void doNextOp(DhcpStateMachine machine) {
- InetAddress clientRequest =
- mRequestedIp == null ? mClientIp : mRequestedIp;
- Log.v(TAG, "requested IP is " + mRequestedIp + " and client IP is " +
- mClientIp);
- machine.onRequestReceived(mBroadcast, mTransId, mClientMac,
- clientRequest, mRequestedParams, mHostName);
- }
-}
diff --git a/core/java/android/net/dhcp/DhcpStateMachine.java b/core/java/android/net/dhcp/DhcpStateMachine.java
deleted file mode 100644
index bc9a798..0000000
--- a/core/java/android/net/dhcp/DhcpStateMachine.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.util.List;
-
-/**
- * This class defines the "next steps" which occur after a given DHCP
- * packet has been received.
- */
-interface DhcpStateMachine {
- /**
- * Signals that an offer packet has been received with the specified
- * parameters.
- */
- public void onOfferReceived(boolean broadcast, int transactionId,
- byte[] myMac, InetAddress offeredIpAddress,
- InetAddress serverIpAddress);
-
- /**
- * Signals that a NAK packet has been received.
- */
- public void onNakReceived();
-
- /**
- * Signals that the final ACK has been received from the server.
- */
- public void onAckReceived(InetAddress myIpAddress, InetAddress myNetMask,
- InetAddress myGateway, List<InetAddress> myDnsServers,
- InetAddress myDhcpServer, int leaseTime);
-
- /**
- * Signals that a client's DISCOVER packet has been received with the
- * specified parameters.
- */
- public void onDiscoverReceived(boolean broadcast, int transactionId,
- byte[] clientMac, byte[] requestedParameterList);
-
- /**
- * Signals that a client's REQUEST packet has been received with the
- * specified parameters.
- */
- public void onRequestReceived(boolean broadcast, int transactionId,
- byte[] clientMac, InetAddress requestedIp, byte[] requestedParams,
- String clientHostName);
-
- /**
- * Signals that a client's INFORM packet has been received with the
- * specified parameters.
- */
- public void onInformReceived(int transactionId, byte[] clientMac,
- InetAddress preassignedIp, byte[] requestedParams);
-
- /**
- * Signals that a client's DECLINE packet has been received with the
- * specified parameters.
- */
- public void onDeclineReceived(byte[] clientMac, InetAddress declinedIp);
-}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d96a0e9..8b3ecae 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -29,8 +29,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
+import android.util.ArrayMap;
import android.util.Printer;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -284,21 +284,21 @@ public abstract class BatteryStats implements Parcelable {
*
* @return a Map from Strings to Uid.Wakelock objects.
*/
- public abstract Map<String, ? extends Wakelock> getWakelockStats();
+ public abstract ArrayMap<String, ? extends Wakelock> getWakelockStats();
/**
* Returns a mapping containing sync statistics.
*
* @return a Map from Strings to Timer objects.
*/
- public abstract Map<String, ? extends Timer> getSyncStats();
+ public abstract ArrayMap<String, ? extends Timer> getSyncStats();
/**
* Returns a mapping containing scheduled job statistics.
*
* @return a Map from Strings to Timer objects.
*/
- public abstract Map<String, ? extends Timer> getJobStats();
+ public abstract ArrayMap<String, ? extends Timer> getJobStats();
/**
* The statistics associated with a particular wake lock.
@@ -324,14 +324,14 @@ public abstract class BatteryStats implements Parcelable {
*
* @return a Map from Strings to Uid.Proc objects.
*/
- public abstract Map<String, ? extends Proc> getProcessStats();
+ public abstract ArrayMap<String, ? extends Proc> getProcessStats();
/**
* Returns a mapping containing package statistics.
*
* @return a Map from Strings to Uid.Pkg objects.
*/
- public abstract Map<String, ? extends Pkg> getPackageStats();
+ public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
/**
* {@hide}
@@ -502,17 +502,16 @@ public abstract class BatteryStats implements Parcelable {
public static abstract class Pkg {
/**
- * Returns the number of times this package has done something that could wake up the
- * device from sleep.
- *
- * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+ * Returns information about all wakeup alarms that have been triggered for this
+ * package. The mapping keys are tag names for the alarms, the counter contains
+ * the number of times the alarm was triggered while on battery.
*/
- public abstract int getWakeups(int which);
+ public abstract ArrayMap<String, ? extends Counter> getWakeupAlarmStats();
/**
* Returns a mapping containing service statistics.
*/
- public abstract Map<String, ? extends Serv> getServiceStats();
+ public abstract ArrayMap<String, ? extends Serv> getServiceStats();
/**
* The statistics associated with a particular service.
@@ -614,6 +613,9 @@ public abstract class BatteryStats implements Parcelable {
if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
out.append('p');
}
+ if ((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+ out.append('i');
+ }
switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
case Display.STATE_OFF: out.append('F'); break;
case Display.STATE_ON: out.append('O'); break;
@@ -623,6 +625,9 @@ public abstract class BatteryStats implements Parcelable {
if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
out.append('P');
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+ out.append('I');
+ }
out.append('-');
appendHex(level, 4, out);
out.append('-');
@@ -649,6 +654,9 @@ public abstract class BatteryStats implements Parcelable {
case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
<< STEP_LEVEL_INITIAL_MODE_SHIFT);
break;
+ case 'i': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+ << STEP_LEVEL_INITIAL_MODE_SHIFT);
+ break;
case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
break;
case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
@@ -661,6 +669,9 @@ public abstract class BatteryStats implements Parcelable {
case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
<< STEP_LEVEL_MODIFIED_MODE_SHIFT);
break;
+ case 'I': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+ << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+ break;
}
}
i++;
@@ -821,11 +832,18 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ public static final class PackageChange {
+ public String mPackageName;
+ public boolean mUpdate;
+ public int mVersionCode;
+ }
+
public static final class DailyItem {
public long mStartTime;
public long mEndTime;
public LevelStepTracker mDischargeSteps;
public LevelStepTracker mChargeSteps;
+ public ArrayList<PackageChange> mPackageChanges;
}
public abstract DailyItem getDailyItemLocked(int daysAgo);
@@ -1044,14 +1062,15 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
- public static final int STATE2_LOW_POWER_FLAG = 1<<31;
+ public static final int STATE2_POWER_SAVE_FLAG = 1<<31;
public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
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 MOST_INTERESTING_STATES2 =
- STATE2_LOW_POWER_FLAG | STATE2_WIFI_ON_FLAG;
+ STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG;
public int states2;
@@ -1086,10 +1105,18 @@ public abstract class BatteryStats implements Parcelable {
public static final int EVENT_USER_RUNNING = 0x0007;
// Events for foreground user.
public static final int EVENT_USER_FOREGROUND = 0x0008;
- // Events for connectivity changed.
+ // Event for connectivity changed.
public static final int EVENT_CONNECTIVITY_CHANGED = 0x0009;
+ // Event for significant motion taking us out of idle mode.
+ public static final int EVENT_SIGNIFICANT_MOTION = 0x000a;
+ // Event for becoming active taking us out of idle mode.
+ public static final int EVENT_ACTIVE = 0x000b;
+ // Event for a package being installed.
+ public static final int EVENT_PACKAGE_INSTALLED = 0x000c;
+ // Event for a package being uninstalled.
+ public static final int EVENT_PACKAGE_UNINSTALLED = 0x000d;
// Number of event types.
- public static final int EVENT_COUNT = 0x000a;
+ public static final int EVENT_COUNT = 0x000e;
// Mask to extract out only the type part of the event.
public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
@@ -1325,7 +1352,7 @@ public abstract class BatteryStats implements Parcelable {
int idx = code&HistoryItem.EVENT_TYPE_MASK;
HashMap<String, SparseIntArray> active = mActiveEvents[idx];
if (active == null) {
- active = new HashMap<String, SparseIntArray>();
+ active = new HashMap<>();
mActiveEvents[idx] = active;
}
SparseIntArray uids = active.get(name);
@@ -1486,19 +1513,51 @@ public abstract class BatteryStats implements Parcelable {
long elapsedRealtimeUs, int which);
/**
- * Returns the time in microseconds that low power mode has been enabled while the device was
+ * Returns the time in microseconds that power save mode has been enabled while the device was
* running on battery.
*
* {@hide}
*/
- public abstract long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which);
+ public abstract long getPowerSaveModeEnabledTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times that power save mode was enabled.
+ *
+ * {@hide}
+ */
+ public abstract int getPowerSaveModeEnabledCount(int which);
+
+ /**
+ * Returns the time in microseconds that device has been in idle mode while
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times that the devie has gone in to idle mode.
+ *
+ * {@hide}
+ */
+ public abstract int getDeviceIdleModeEnabledCount(int which);
/**
- * Returns the number of times that low power mode was enabled.
+ * Returns the time in microseconds that device has been in idling while on
+ * battery. This is broader than {@link #getDeviceIdleModeEnabledTime} -- it
+ * counts all of the time that we consider the device to be idle, whether or not
+ * it is currently in the actual device idle mode.
*
* {@hide}
*/
- public abstract int getLowPowerModeEnabledCount(int which);
+ public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times that the devie has started idling.
+ *
+ * {@hide}
+ */
+ public abstract int getDeviceIdlingCount(int which);
/**
* Returns the number of times that connectivity state changed.
@@ -1692,11 +1751,12 @@ public abstract class BatteryStats implements Parcelable {
public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
= new BitDescription[] {
- new BitDescription(HistoryItem.STATE2_LOW_POWER_FLAG, "low_power", "lp"),
+ 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_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_WIFI_SIGNAL_STRENGTH_MASK,
HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
new String[] { "0", "1", "2", "3", "4" },
@@ -1707,11 +1767,13 @@ 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"
+ "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
+ "motion", "active", "pkginst", "pkgunin"
};
public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
- "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn"
+ "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
+ "Esm", "Eac", "Epi", "Epu"
};
/**
@@ -2043,45 +2105,44 @@ public abstract class BatteryStats implements Parcelable {
// Step duration mode: power save is on.
public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
+ // Step duration mode: device is currently in idle mode.
+ public static final int STEP_LEVEL_MODE_DEVICE_IDLE = 0x08;
+
public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
- STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
- STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
};
public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
(Display.STATE_OFF-1),
(Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_OFF-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
(Display.STATE_ON-1),
(Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
(Display.STATE_DOZE-1),
(Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
(Display.STATE_DOZE_SUSPEND-1),
(Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
};
public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
"screen off",
"screen off power save",
+ "screen off device idle",
"screen on",
"screen on power save",
"screen doze",
"screen doze power save",
"screen doze-suspend",
"screen doze-suspend power save",
- };
- public static final String[] STEP_LEVEL_MODE_TAGS = new String[] {
- "off",
- "off-save",
- "on",
- "on-save",
- "doze",
- "doze-save",
- "susp",
- "susp-save",
+ "screen doze-suspend device idle",
};
/**
@@ -2114,6 +2175,8 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract LevelStepTracker getDailyChargeLevelStepTracker();
+ public abstract ArrayList<PackageChange> getDailyPackageChanges();
+
public abstract Map<String, ? extends Timer> getWakeupReasonStats();
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -2310,19 +2373,21 @@ public abstract class BatteryStats implements Parcelable {
final long totalUptime = computeUptime(rawUptime, which);
final long screenOnTime = getScreenOnTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
- final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which);
+ final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
+ final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+ 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);
- StringBuilder sb = new StringBuilder(128);
+ final StringBuilder sb = new StringBuilder(128);
- SparseArray<? extends Uid> uidStats = getUidStats();
+ final SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
- String category = STAT_NAMES[which];
+ final String category = STAT_NAMES[which];
// Dump "battery" stat
dumpLine(pw, 0 /* uid */, category, BATTERY_DATA,
@@ -2337,37 +2402,35 @@ public abstract class BatteryStats implements Parcelable {
long partialWakeLockTimeTotal = 0;
for (int iu = 0; iu < NU; iu++) {
- Uid u = uidStats.valueAt(iu);
-
- Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
- if (wakelocks.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
- : wakelocks.entrySet()) {
- Uid.Wakelock wl = ent.getValue();
-
- Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
- if (fullWakeTimer != null) {
- fullWakeLockTimeTotal += fullWakeTimer.getTotalTimeLocked(rawRealtime,
- which);
- }
+ final Uid u = uidStats.valueAt(iu);
- Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
- if (partialWakeTimer != null) {
- partialWakeLockTimeTotal += partialWakeTimer.getTotalTimeLocked(
- rawRealtime, which);
- }
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+ = u.getWakelockStats();
+ for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+ final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+ if (fullWakeTimer != null) {
+ fullWakeLockTimeTotal += fullWakeTimer.getTotalTimeLocked(rawRealtime,
+ which);
+ }
+
+ final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+ if (partialWakeTimer != null) {
+ partialWakeLockTimeTotal += partialWakeTimer.getTotalTimeLocked(
+ rawRealtime, which);
}
}
}
- long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
- long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
- long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
- long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
- long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
- long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+ 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);
+ final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+ final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+ 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,
@@ -2382,7 +2445,9 @@ public abstract class BatteryStats implements Parcelable {
fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000,
0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
- lowPowerModeEnabledTime / 1000, connChanges);
+ powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
+ getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
+ getDeviceIdlingCount(which));
// Dump screen brightness stats
Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -2477,7 +2542,7 @@ public abstract class BatteryStats implements Parcelable {
}
if (reqUid < 0) {
- Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+ final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
if (kernelWakelocks.size() > 0) {
for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
sb.setLength(0);
@@ -2486,7 +2551,7 @@ public abstract class BatteryStats implements Parcelable {
sb.toString());
}
}
- Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+ final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
if (wakeupReasons.size() > 0) {
for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
// Not doing the regular wake lock formatting to remain compatible
@@ -2499,10 +2564,10 @@ public abstract class BatteryStats implements Parcelable {
}
}
- BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+ final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
helper.create(this);
helper.refreshStats(which, UserHandle.USER_ALL);
- List<BatterySipper> sippers = helper.getUsageList();
+ final List<BatterySipper> sippers = helper.getUsageList();
if (sippers != null && sippers.size() > 0) {
dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
BatteryStatsHelper.makemAh(helper.getPowerProfile().getBatteryCapacity()),
@@ -2510,7 +2575,7 @@ public abstract class BatteryStats implements Parcelable {
BatteryStatsHelper.makemAh(helper.getMinDrainedPower()),
BatteryStatsHelper.makemAh(helper.getMaxDrainedPower()));
for (int i=0; i<sippers.size(); i++) {
- BatterySipper bs = sippers.get(i);
+ final BatterySipper bs = sippers.get(i);
int uid = 0;
String label;
switch (bs.drainType) {
@@ -2562,22 +2627,22 @@ public abstract class BatteryStats implements Parcelable {
if (reqUid >= 0 && uid != reqUid) {
continue;
}
- Uid u = uidStats.valueAt(iu);
+ final Uid u = uidStats.valueAt(iu);
// Dump Network stats per uid, if any
- long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
- long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
- long wifiBytesRx = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
- long wifiBytesTx = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
- long mobilePacketsRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
- long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
- long mobileActiveTime = u.getMobileRadioActiveTime(which);
- int mobileActiveCount = u.getMobileRadioActiveCount(which);
- long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
- long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
- long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
- long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
- int wifiScanCount = u.getWifiScanCount(which);
- long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+ final long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+ final long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+ final long wifiBytesRx = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+ final long wifiBytesTx = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+ final long mobilePacketsRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+ final long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+ final long mobileActiveTime = u.getMobileRadioActiveTime(which);
+ 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
@@ -2608,93 +2673,90 @@ public abstract class BatteryStats implements Parcelable {
}
}
- Map<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
- if (wakelocks.size() > 0) {
- for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
- Uid.Wakelock wl = ent.getValue();
- String linePrefix = "";
- sb.setLength(0);
- linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL),
- rawRealtime, "f", which, linePrefix);
- linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL),
- rawRealtime, "p", which, linePrefix);
- linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
- rawRealtime, "w", which, linePrefix);
-
- // Only log if we had at lease one wakelock...
- if (sb.length() > 0) {
- String name = ent.getKey();
- if (name.indexOf(',') >= 0) {
- name = name.replace(',', '_');
- }
- dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
+ final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+ for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+ String linePrefix = "";
+ sb.setLength(0);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL),
+ rawRealtime, "f", which, linePrefix);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL),
+ rawRealtime, "p", which, linePrefix);
+ linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
+ rawRealtime, "w", which, linePrefix);
+
+ // Only log if we had at lease one wakelock...
+ if (sb.length() > 0) {
+ String name = wakelocks.keyAt(iw);
+ if (name.indexOf(',') >= 0) {
+ name = name.replace(',', '_');
}
+ dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
}
}
- Map<String, ? extends Timer> syncs = u.getSyncStats();
- if (syncs.size() > 0) {
- for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
- Timer timer = ent.getValue();
- // Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
- if (totalTime != 0) {
- dumpLine(pw, uid, category, SYNC_DATA, ent.getKey(), totalTime, count);
- }
+ final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+ for (int isy=syncs.size()-1; isy>=0; isy--) {
+ final Timer timer = syncs.valueAt(isy);
+ // Convert from microseconds to milliseconds with rounding
+ final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+ final int count = timer.getCountLocked(which);
+ if (totalTime != 0) {
+ dumpLine(pw, uid, category, SYNC_DATA, syncs.keyAt(isy), totalTime, count);
}
}
- Map<String, ? extends Timer> jobs = u.getJobStats();
- if (jobs.size() > 0) {
- for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
- Timer timer = ent.getValue();
- // Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
- if (totalTime != 0) {
- dumpLine(pw, uid, category, JOB_DATA, ent.getKey(), totalTime, count);
- }
+ final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+ for (int ij=jobs.size()-1; ij>=0; ij--) {
+ final Timer timer = jobs.valueAt(ij);
+ // Convert from microseconds to milliseconds with rounding
+ final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+ final int count = timer.getCountLocked(which);
+ if (totalTime != 0) {
+ dumpLine(pw, uid, category, JOB_DATA, jobs.keyAt(ij), totalTime, count);
}
}
- SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
- int NSE = sensors.size();
+ final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ final int NSE = sensors.size();
for (int ise=0; ise<NSE; ise++) {
- Uid.Sensor se = sensors.valueAt(ise);
- int sensorNumber = sensors.keyAt(ise);
- Timer timer = se.getSensorTime();
+ final Uid.Sensor se = sensors.valueAt(ise);
+ final int sensorNumber = sensors.keyAt(ise);
+ final Timer timer = se.getSensorTime();
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
+ final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
+ / 1000;
+ final int count = timer.getCountLocked(which);
if (totalTime != 0) {
dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count);
}
}
}
- Timer vibTimer = u.getVibratorOnTimer();
+ final Timer vibTimer = u.getVibratorOnTimer();
if (vibTimer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (vibTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = vibTimer.getCountLocked(which);
+ final long totalTime = (vibTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+ / 1000;
+ final int count = vibTimer.getCountLocked(which);
if (totalTime != 0) {
dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count);
}
}
- Timer fgTimer = u.getForegroundActivityTimer();
+ final Timer fgTimer = u.getForegroundActivityTimer();
if (fgTimer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = fgTimer.getCountLocked(which);
+ final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+ / 1000;
+ final int count = fgTimer.getCountLocked(which);
if (totalTime != 0) {
dumpLine(pw, uid, category, FOREGROUND_DATA, totalTime, count);
}
}
- Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
+ final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
long totalStateTime = 0;
for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
totalStateTime += u.getProcessStateTime(ips, rawRealtime, which);
@@ -2704,50 +2766,48 @@ public abstract class BatteryStats implements Parcelable {
dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
}
- Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
- if (processStats.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
- : processStats.entrySet()) {
- Uid.Proc ps = ent.getValue();
-
- final long userMillis = ps.getUserTime(which);
- final long systemMillis = ps.getSystemTime(which);
- final long foregroundMillis = ps.getForegroundTime(which);
- final int starts = ps.getStarts(which);
- final int numCrashes = ps.getNumCrashes(which);
- final int numAnrs = ps.getNumAnrs(which);
-
- if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
- || starts != 0 || numAnrs != 0 || numCrashes != 0) {
- dumpLine(pw, uid, category, PROCESS_DATA, ent.getKey(), userMillis,
- systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
- }
+ final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+ = u.getProcessStats();
+ for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+ final Uid.Proc ps = processStats.valueAt(ipr);
+
+ final long userMillis = ps.getUserTime(which);
+ final long systemMillis = ps.getSystemTime(which);
+ final long foregroundMillis = ps.getForegroundTime(which);
+ final int starts = ps.getStarts(which);
+ final int numCrashes = ps.getNumCrashes(which);
+ final int numAnrs = ps.getNumAnrs(which);
+
+ if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
+ || starts != 0 || numAnrs != 0 || numCrashes != 0) {
+ dumpLine(pw, uid, category, PROCESS_DATA, processStats.keyAt(ipr), userMillis,
+ systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
}
}
- Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
- if (packageStats.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
- : packageStats.entrySet()) {
-
- Uid.Pkg ps = ent.getValue();
- int wakeups = ps.getWakeups(which);
- Map<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
- for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
- : serviceStats.entrySet()) {
- BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
- long startTime = ss.getStartTime(batteryUptime, which);
- int starts = ss.getStarts(which);
- int launches = ss.getLaunches(which);
- if (startTime != 0 || starts != 0 || launches != 0) {
- dumpLine(pw, uid, category, APK_DATA,
- wakeups, // wakeup alarms
- ent.getKey(), // Apk
- sent.getKey(), // service
- startTime / 1000, // time spent started, in ms
- starts,
- launches);
- }
+ final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+ = u.getPackageStats();
+ for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+ final Uid.Pkg ps = packageStats.valueAt(ipkg);
+ int wakeups = 0;
+ final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+ for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+ wakeups += alarms.valueAt(iwa).getCountLocked(which);
+ }
+ final ArrayMap<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+ for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+ final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+ final long startTime = ss.getStartTime(batteryUptime, which);
+ final int starts = ss.getStarts(which);
+ final int launches = ss.getLaunches(which);
+ if (startTime != 0 || starts != 0 || launches != 0) {
+ dumpLine(pw, uid, category, APK_DATA,
+ wakeups, // wakeup alarms
+ packageStats.keyAt(ipkg), // Apk
+ serviceStats.keyAt(isvc), // service
+ startTime / 1000, // time spent started, in ms
+ starts,
+ launches);
}
}
}
@@ -2796,9 +2856,9 @@ public abstract class BatteryStats implements Parcelable {
final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
- StringBuilder sb = new StringBuilder(128);
+ final StringBuilder sb = new StringBuilder(128);
- SparseArray<? extends Uid> uidStats = getUidStats();
+ final SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
sb.setLength(0);
@@ -2849,7 +2909,9 @@ public abstract class BatteryStats implements Parcelable {
final long screenOnTime = getScreenOnTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
- final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which);
+ final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
+ final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+ final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2884,24 +2946,46 @@ public abstract class BatteryStats implements Parcelable {
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- if (lowPowerModeEnabledTime != 0) {
+ if (powerSaveModeEnabledTime != 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Low power mode enabled: ");
- formatTimeMs(sb, lowPowerModeEnabledTime / 1000);
+ sb.append(" Power save mode enabled: ");
+ formatTimeMs(sb, powerSaveModeEnabledTime / 1000);
sb.append("(");
- sb.append(formatRatioLocked(lowPowerModeEnabledTime, whichBatteryRealtime));
+ sb.append(formatRatioLocked(powerSaveModeEnabledTime, whichBatteryRealtime));
sb.append(")");
pw.println(sb.toString());
}
+ if (deviceIdlingTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Device idling: ");
+ formatTimeMs(sb, deviceIdlingTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getDeviceIdlingCount(which));
+ sb.append("x");
+ pw.println(sb.toString());
+ }
+ if (deviceIdleModeEnabledTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Idle mode time: ");
+ formatTimeMs(sb, deviceIdleModeEnabledTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getDeviceIdleModeEnabledCount(which));
+ sb.append("x");
+ pw.println(sb.toString());
+ }
if (phoneOnTime != 0) {
sb.setLength(0);
sb.append(prefix);
sb.append(" Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000);
sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime));
- sb.append(") "); sb.append(getPhoneOnCount(which));
+ sb.append(") "); sb.append(getPhoneOnCount(which)); sb.append("x");
}
- int connChanges = getNumConnectivityChange(which);
+ final int connChanges = getNumConnectivityChange(which);
if (connChanges != 0) {
pw.print(prefix);
pw.print(" Connectivity changes: "); pw.println(connChanges);
@@ -2911,50 +2995,48 @@ public abstract class BatteryStats implements Parcelable {
long fullWakeLockTimeTotalMicros = 0;
long partialWakeLockTimeTotalMicros = 0;
- final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
+ final ArrayList<TimerEntry> timers = new ArrayList<>();
for (int iu = 0; iu < NU; iu++) {
- Uid u = uidStats.valueAt(iu);
-
- Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
- if (wakelocks.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
- : wakelocks.entrySet()) {
- Uid.Wakelock wl = ent.getValue();
-
- Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
- if (fullWakeTimer != null) {
- fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTimeLocked(
- rawRealtime, which);
- }
+ final Uid u = uidStats.valueAt(iu);
- Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
- if (partialWakeTimer != null) {
- long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
- rawRealtime, which);
- if (totalTimeMicros > 0) {
- if (reqUid < 0) {
- // Only show the ordered list of all wake
- // locks if the caller is not asking for data
- // about a specific uid.
- timers.add(new TimerEntry(ent.getKey(), u.getUid(),
- partialWakeTimer, totalTimeMicros));
- }
- partialWakeLockTimeTotalMicros += totalTimeMicros;
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+ = u.getWakelockStats();
+ for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+ final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+ if (fullWakeTimer != null) {
+ fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTimeLocked(
+ rawRealtime, which);
+ }
+
+ final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+ if (partialWakeTimer != null) {
+ final long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
+ rawRealtime, which);
+ if (totalTimeMicros > 0) {
+ if (reqUid < 0) {
+ // Only show the ordered list of all wake
+ // locks if the caller is not asking for data
+ // about a specific uid.
+ timers.add(new TimerEntry(wakelocks.keyAt(iw), u.getUid(),
+ partialWakeTimer, totalTimeMicros));
}
+ partialWakeLockTimeTotalMicros += totalTimeMicros;
}
}
}
}
- long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
- long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
- long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
- long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
- long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
- long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+ 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);
+ final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+ final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+ 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);
if (fullWakeLockTimeTotalMicros != 0) {
sb.setLength(0);
@@ -3151,9 +3233,9 @@ public abstract class BatteryStats implements Parcelable {
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- final long wifiIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
- final long wifiRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
- final long wifiTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, 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 wifiTotalTimeMs = wifiIdleTimeMs + wifiRxTimeMs + wifiTxTimeMs;
sb.setLength(0);
@@ -3276,7 +3358,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println();
}
- BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+ final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
helper.create(this);
helper.refreshStats(which, UserHandle.USER_ALL);
List<BatterySipper> sippers = helper.getUsageList();
@@ -3291,7 +3373,7 @@ public abstract class BatteryStats implements Parcelable {
}
pw.println();
for (int i=0; i<sippers.size(); i++) {
- BatterySipper bs = sippers.get(i);
+ final BatterySipper bs = sippers.get(i);
switch (bs.drainType) {
case IDLE:
pw.print(prefix); pw.print(" Idle: "); printmAh(pw, bs.value);
@@ -3348,7 +3430,7 @@ public abstract class BatteryStats implements Parcelable {
pw.print(prefix); pw.println(" Per-app mobile ms per packet:");
long totalTime = 0;
for (int i=0; i<sippers.size(); i++) {
- BatterySipper bs = sippers.get(i);
+ final BatterySipper bs = sippers.get(i);
sb.setLength(0);
sb.append(prefix); sb.append(" Uid ");
UserHandle.formatUid(sb, bs.uidObj.getUid());
@@ -3385,12 +3467,14 @@ public abstract class BatteryStats implements Parcelable {
};
if (reqUid < 0) {
- Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+ final Map<String, ? extends BatteryStats.Timer> kernelWakelocks
+ = getKernelWakelockStats();
if (kernelWakelocks.size() > 0) {
- final ArrayList<TimerEntry> ktimers = new ArrayList<TimerEntry>();
- for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
- BatteryStats.Timer timer = ent.getValue();
- long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+ final ArrayList<TimerEntry> ktimers = new ArrayList<>();
+ for (Map.Entry<String, ? extends BatteryStats.Timer> ent
+ : kernelWakelocks.entrySet()) {
+ final BatteryStats.Timer timer = ent.getValue();
+ final long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
if (totalTimeMillis > 0) {
ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
}
@@ -3399,7 +3483,7 @@ public abstract class BatteryStats implements Parcelable {
Collections.sort(ktimers, timerComparator);
pw.print(prefix); pw.println(" All kernel wake locks:");
for (int i=0; i<ktimers.size(); i++) {
- TimerEntry timer = ktimers.get(i);
+ final TimerEntry timer = ktimers.get(i);
String linePrefix = ": ";
sb.setLength(0);
sb.append(prefix);
@@ -3435,12 +3519,12 @@ public abstract class BatteryStats implements Parcelable {
pw.println();
}
- Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+ final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
if (wakeupReasons.size() > 0) {
pw.print(prefix); pw.println(" All wakeup reasons:");
- final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+ final ArrayList<TimerEntry> reasons = new ArrayList<>();
for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
- Timer timer = ent.getValue();
+ final Timer timer = ent.getValue();
reasons.add(new TimerEntry(ent.getKey(), 0, timer,
timer.getCountLocked(which)));
}
@@ -3466,7 +3550,7 @@ public abstract class BatteryStats implements Parcelable {
continue;
}
- Uid u = uidStats.valueAt(iu);
+ final Uid u = uidStats.valueAt(iu);
pw.print(prefix);
pw.print(" ");
@@ -3474,20 +3558,20 @@ public abstract class BatteryStats implements Parcelable {
pw.println(":");
boolean uidActivity = false;
- long mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
- long wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
- long wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
- long mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
- long mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
- long uidMobileActiveTime = u.getMobileRadioActiveTime(which);
- int uidMobileActiveCount = u.getMobileRadioActiveCount(which);
- long wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
- long wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
- long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
- long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
- int wifiScanCount = u.getWifiScanCount(which);
- long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+ final long mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+ final long mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+ final long wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+ final long wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+ final long mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+ final long mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+ final long uidMobileActiveTime = u.getMobileRadioActiveTime(which);
+ final int uidMobileActiveCount = u.getMobileRadioActiveCount(which);
+ final long wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+ final long wifiTxPackets = 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 (mobileRxBytes > 0 || mobileTxBytes > 0
|| mobileRxPackets > 0 || mobileTxPackets > 0) {
@@ -3545,7 +3629,7 @@ public abstract class BatteryStats implements Parcelable {
if (u.hasUserActivity()) {
boolean hasData = false;
for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
- int val = u.getUserActivityCount(i, which);
+ final int val = u.getUserActivityCount(i, which);
if (val != 0) {
if (!hasData) {
sb.setLength(0);
@@ -3564,125 +3648,121 @@ public abstract class BatteryStats implements Parcelable {
}
}
- Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
- if (wakelocks.size() > 0) {
- long totalFull = 0, totalPartial = 0, totalWindow = 0;
- int count = 0;
- for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
- Uid.Wakelock wl = ent.getValue();
- String linePrefix = ": ";
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+ = u.getWakelockStats();
+ long totalFullWakelock = 0, totalPartialWakelock = 0, totalWindowWakelock = 0;
+ int countWakelock = 0;
+ for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+ String linePrefix = ": ";
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Wake lock ");
+ sb.append(wakelocks.keyAt(iw));
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), rawRealtime,
+ "full", which, linePrefix);
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), rawRealtime,
+ "partial", which, linePrefix);
+ linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), rawRealtime,
+ "window", which, linePrefix);
+ if (true || !linePrefix.equals(": ")) {
+ sb.append(" realtime");
+ // Only print out wake locks that were held
+ pw.println(sb.toString());
+ uidActivity = true;
+ countWakelock++;
+ }
+ totalFullWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
+ rawRealtime, which);
+ totalPartialWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
+ rawRealtime, which);
+ totalWindowWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
+ rawRealtime, which);
+ }
+ if (countWakelock > 1) {
+ if (totalFullWakelock != 0 || totalPartialWakelock != 0
+ || totalWindowWakelock != 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Wake lock ");
- sb.append(ent.getKey());
- linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), rawRealtime,
- "full", which, linePrefix);
- linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), rawRealtime,
- "partial", which, linePrefix);
- linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), rawRealtime,
- "window", which, linePrefix);
- if (true || !linePrefix.equals(": ")) {
- sb.append(" realtime");
- // Only print out wake locks that were held
- pw.println(sb.toString());
- uidActivity = true;
- count++;
+ sb.append(" TOTAL wake: ");
+ boolean needComma = false;
+ if (totalFullWakelock != 0) {
+ needComma = true;
+ formatTimeMs(sb, totalFullWakelock);
+ sb.append("full");
}
- totalFull += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
- rawRealtime, which);
- totalPartial += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
- rawRealtime, which);
- totalWindow += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
- rawRealtime, which);
- }
- if (count > 1) {
- if (totalFull != 0 || totalPartial != 0 || totalWindow != 0) {
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" TOTAL wake: ");
- boolean needComma = false;
- if (totalFull != 0) {
- needComma = true;
- formatTimeMs(sb, totalFull);
- sb.append("full");
- }
- if (totalPartial != 0) {
- if (needComma) {
- sb.append(", ");
- }
- needComma = true;
- formatTimeMs(sb, totalPartial);
- sb.append("partial");
+ if (totalPartialWakelock != 0) {
+ if (needComma) {
+ sb.append(", ");
}
- if (totalWindow != 0) {
- if (needComma) {
- sb.append(", ");
- }
- needComma = true;
- formatTimeMs(sb, totalWindow);
- sb.append("window");
+ needComma = true;
+ formatTimeMs(sb, totalPartialWakelock);
+ sb.append("partial");
+ }
+ if (totalWindowWakelock != 0) {
+ if (needComma) {
+ sb.append(", ");
}
- sb.append(" realtime");
- pw.println(sb.toString());
+ needComma = true;
+ formatTimeMs(sb, totalWindowWakelock);
+ sb.append("window");
}
+ sb.append(" realtime");
+ pw.println(sb.toString());
}
}
- Map<String, ? extends Timer> syncs = u.getSyncStats();
- if (syncs.size() > 0) {
- for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
- Timer timer = ent.getValue();
- // Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Sync ");
- sb.append(ent.getKey());
- sb.append(": ");
- if (totalTime != 0) {
- formatTimeMs(sb, totalTime);
- sb.append("realtime (");
- sb.append(count);
- sb.append(" times)");
- } else {
- sb.append("(not used)");
- }
- pw.println(sb.toString());
- uidActivity = true;
+ final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+ for (int isy=syncs.size()-1; isy>=0; isy--) {
+ final Timer timer = syncs.valueAt(isy);
+ // Convert from microseconds to milliseconds with rounding
+ final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+ final int count = timer.getCountLocked(which);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Sync ");
+ sb.append(syncs.keyAt(isy));
+ sb.append(": ");
+ if (totalTime != 0) {
+ formatTimeMs(sb, totalTime);
+ sb.append("realtime (");
+ sb.append(count);
+ sb.append(" times)");
+ } else {
+ sb.append("(not used)");
}
+ pw.println(sb.toString());
+ uidActivity = true;
}
- Map<String, ? extends Timer> jobs = u.getJobStats();
- if (jobs.size() > 0) {
- for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
- Timer timer = ent.getValue();
- // Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Job ");
- sb.append(ent.getKey());
- sb.append(": ");
- if (totalTime != 0) {
- formatTimeMs(sb, totalTime);
- sb.append("realtime (");
- sb.append(count);
- sb.append(" times)");
- } else {
- sb.append("(not used)");
- }
- pw.println(sb.toString());
- uidActivity = true;
+ final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+ for (int ij=jobs.size()-1; ij>=0; ij--) {
+ final Timer timer = jobs.valueAt(ij);
+ // Convert from microseconds to milliseconds with rounding
+ final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+ final int count = timer.getCountLocked(which);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Job ");
+ sb.append(jobs.keyAt(ij));
+ sb.append(": ");
+ if (totalTime != 0) {
+ formatTimeMs(sb, totalTime);
+ sb.append("realtime (");
+ sb.append(count);
+ sb.append(" times)");
+ } else {
+ sb.append("(not used)");
}
+ pw.println(sb.toString());
+ uidActivity = true;
}
- SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
- int NSE = sensors.size();
+ final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ final int NSE = sensors.size();
for (int ise=0; ise<NSE; ise++) {
- Uid.Sensor se = sensors.valueAt(ise);
- int sensorNumber = sensors.keyAt(ise);
+ final Uid.Sensor se = sensors.valueAt(ise);
+ final int sensorNumber = sensors.keyAt(ise);
sb.setLength(0);
sb.append(prefix);
sb.append(" Sensor ");
@@ -3694,12 +3774,12 @@ public abstract class BatteryStats implements Parcelable {
}
sb.append(": ");
- Timer timer = se.getSensorTime();
+ final Timer timer = se.getSensorTime();
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (timer.getTotalTimeLocked(
+ final long totalTime = (timer.getTotalTimeLocked(
rawRealtime, which) + 500) / 1000;
- int count = timer.getCountLocked(which);
+ final int count = timer.getCountLocked(which);
//timer.logState();
if (totalTime != 0) {
formatTimeMs(sb, totalTime);
@@ -3717,12 +3797,12 @@ public abstract class BatteryStats implements Parcelable {
uidActivity = true;
}
- Timer vibTimer = u.getVibratorOnTimer();
+ final Timer vibTimer = u.getVibratorOnTimer();
if (vibTimer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (vibTimer.getTotalTimeLocked(
+ final long totalTime = (vibTimer.getTotalTimeLocked(
rawRealtime, which) + 500) / 1000;
- int count = vibTimer.getCountLocked(which);
+ final int count = vibTimer.getCountLocked(which);
//timer.logState();
if (totalTime != 0) {
sb.setLength(0);
@@ -3737,11 +3817,12 @@ public abstract class BatteryStats implements Parcelable {
}
}
- Timer fgTimer = u.getForegroundActivityTimer();
+ final Timer fgTimer = u.getForegroundActivityTimer();
if (fgTimer != null) {
// Convert from microseconds to milliseconds with rounding
- long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
- int count = fgTimer.getCountLocked(which);
+ final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+ / 1000;
+ final int count = fgTimer.getCountLocked(which);
if (totalTime != 0) {
sb.setLength(0);
sb.append(prefix);
@@ -3771,125 +3852,121 @@ public abstract class BatteryStats implements Parcelable {
}
}
- Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
- if (processStats.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
- : processStats.entrySet()) {
- Uid.Proc ps = ent.getValue();
- long userTime;
- long systemTime;
- long foregroundTime;
- int starts;
- int numExcessive;
-
- userTime = ps.getUserTime(which);
- systemTime = ps.getSystemTime(which);
- foregroundTime = ps.getForegroundTime(which);
- starts = ps.getStarts(which);
- final int numCrashes = ps.getNumCrashes(which);
- final int numAnrs = ps.getNumAnrs(which);
- numExcessive = which == STATS_SINCE_CHARGED
- ? ps.countExcessivePowers() : 0;
-
- if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
- || numExcessive != 0 || numCrashes != 0 || numAnrs != 0) {
- sb.setLength(0);
- sb.append(prefix); sb.append(" Proc ");
- sb.append(ent.getKey()); sb.append(":\n");
- sb.append(prefix); sb.append(" CPU: ");
- formatTimeMs(sb, userTime); sb.append("usr + ");
- formatTimeMs(sb, systemTime); sb.append("krn ; ");
- formatTimeMs(sb, foregroundTime); sb.append("fg");
- if (starts != 0 || numCrashes != 0 || numAnrs != 0) {
- sb.append("\n"); sb.append(prefix); sb.append(" ");
- boolean hasOne = false;
- if (starts != 0) {
- hasOne = true;
- sb.append(starts); sb.append(" starts");
- }
- if (numCrashes != 0) {
- if (hasOne) {
- sb.append(", ");
- }
- hasOne = true;
- sb.append(numCrashes); sb.append(" crashes");
- }
- if (numAnrs != 0) {
- if (hasOne) {
- sb.append(", ");
- }
- sb.append(numAnrs); sb.append(" anrs");
+ final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+ = u.getProcessStats();
+ for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+ final Uid.Proc ps = processStats.valueAt(ipr);
+ long userTime;
+ long systemTime;
+ long foregroundTime;
+ int starts;
+ int numExcessive;
+
+ userTime = ps.getUserTime(which);
+ systemTime = ps.getSystemTime(which);
+ foregroundTime = ps.getForegroundTime(which);
+ starts = ps.getStarts(which);
+ final int numCrashes = ps.getNumCrashes(which);
+ final int numAnrs = ps.getNumAnrs(which);
+ numExcessive = which == STATS_SINCE_CHARGED
+ ? ps.countExcessivePowers() : 0;
+
+ if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
+ || numExcessive != 0 || numCrashes != 0 || numAnrs != 0) {
+ sb.setLength(0);
+ sb.append(prefix); sb.append(" Proc ");
+ sb.append(processStats.keyAt(ipr)); sb.append(":\n");
+ sb.append(prefix); sb.append(" CPU: ");
+ formatTimeMs(sb, userTime); sb.append("usr + ");
+ formatTimeMs(sb, systemTime); sb.append("krn ; ");
+ formatTimeMs(sb, foregroundTime); sb.append("fg");
+ if (starts != 0 || numCrashes != 0 || numAnrs != 0) {
+ sb.append("\n"); sb.append(prefix); sb.append(" ");
+ boolean hasOne = false;
+ if (starts != 0) {
+ hasOne = true;
+ sb.append(starts); sb.append(" starts");
+ }
+ if (numCrashes != 0) {
+ if (hasOne) {
+ sb.append(", ");
}
+ hasOne = true;
+ sb.append(numCrashes); sb.append(" crashes");
}
- pw.println(sb.toString());
- for (int e=0; e<numExcessive; e++) {
- Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
- if (ew != null) {
- pw.print(prefix); pw.print(" * Killed for ");
- if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
- pw.print("wake lock");
- } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
- pw.print("cpu");
- } else {
- pw.print("unknown");
- }
- pw.print(" use: ");
- TimeUtils.formatDuration(ew.usedTime, pw);
- pw.print(" over ");
- TimeUtils.formatDuration(ew.overTime, pw);
- if (ew.overTime != 0) {
- pw.print(" (");
- pw.print((ew.usedTime*100)/ew.overTime);
- pw.println("%)");
- }
+ if (numAnrs != 0) {
+ if (hasOne) {
+ sb.append(", ");
}
+ sb.append(numAnrs); sb.append(" anrs");
+ }
+ }
+ pw.println(sb.toString());
+ for (int e=0; e<numExcessive; e++) {
+ Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
+ if (ew != null) {
+ pw.print(prefix); pw.print(" * Killed for ");
+ if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
+ pw.print("wake lock");
+ } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
+ pw.print("cpu");
+ } else {
+ pw.print("unknown");
+ }
+ pw.print(" use: ");
+ TimeUtils.formatDuration(ew.usedTime, pw);
+ pw.print(" over ");
+ TimeUtils.formatDuration(ew.overTime, pw);
+ if (ew.overTime != 0) {
+ pw.print(" (");
+ pw.print((ew.usedTime*100)/ew.overTime);
+ pw.println("%)");
+ }
}
- uidActivity = true;
}
+ uidActivity = true;
}
}
- Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
- if (packageStats.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
- : packageStats.entrySet()) {
- pw.print(prefix); pw.print(" Apk "); pw.print(ent.getKey()); pw.println(":");
- boolean apkActivity = false;
- Uid.Pkg ps = ent.getValue();
- int wakeups = ps.getWakeups(which);
- if (wakeups != 0) {
- pw.print(prefix); pw.print(" ");
- pw.print(wakeups); pw.println(" wakeup alarms");
+ final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+ = u.getPackageStats();
+ for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+ pw.print(prefix); pw.print(" Apk "); pw.print(packageStats.keyAt(ipkg));
+ pw.println(":");
+ boolean apkActivity = false;
+ final Uid.Pkg ps = packageStats.valueAt(ipkg);
+ final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+ for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+ pw.print(prefix); pw.print(" Wakeup alarm ");
+ pw.print(alarms.keyAt(iwa)); pw.print(": ");
+ pw.print(alarms.valueAt(iwa).getCountLocked(which));
+ pw.println(" times");
+ apkActivity = true;
+ }
+ final ArrayMap<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+ for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+ final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+ final long startTime = ss.getStartTime(batteryUptime, which);
+ final int starts = ss.getStarts(which);
+ final int launches = ss.getLaunches(which);
+ if (startTime != 0 || starts != 0 || launches != 0) {
+ sb.setLength(0);
+ sb.append(prefix); sb.append(" Service ");
+ sb.append(serviceStats.keyAt(isvc)); sb.append(":\n");
+ sb.append(prefix); sb.append(" Created for: ");
+ formatTimeMs(sb, startTime / 1000);
+ sb.append("uptime\n");
+ sb.append(prefix); sb.append(" Starts: ");
+ sb.append(starts);
+ sb.append(", launches: "); sb.append(launches);
+ pw.println(sb.toString());
apkActivity = true;
}
- Map<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
- if (serviceStats.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
- : serviceStats.entrySet()) {
- BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
- long startTime = ss.getStartTime(batteryUptime, which);
- int starts = ss.getStarts(which);
- int launches = ss.getLaunches(which);
- if (startTime != 0 || starts != 0 || launches != 0) {
- sb.setLength(0);
- sb.append(prefix); sb.append(" Service ");
- sb.append(sent.getKey()); sb.append(":\n");
- sb.append(prefix); sb.append(" Created for: ");
- formatTimeMs(sb, startTime / 1000);
- sb.append("uptime\n");
- sb.append(prefix); sb.append(" Starts: ");
- sb.append(starts);
- sb.append(", launches: "); sb.append(launches);
- pw.println(sb.toString());
- apkActivity = true;
- }
- }
- }
- if (!apkActivity) {
- pw.print(prefix); pw.println(" (nothing executed)");
- }
- uidActivity = true;
}
+ if (!apkActivity) {
+ pw.print(prefix); pw.println(" (nothing executed)");
+ }
+ uidActivity = true;
}
if (!uidActivity) {
pw.print(prefix); pw.println(" (nothing executed)");
@@ -4363,6 +4440,11 @@ public abstract class BatteryStats implements Parcelable {
} else {
lineArgs[3] = "";
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+ lineArgs[3] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-";
+ } else {
+ lineArgs[3] = "";
+ }
dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
} else {
pw.print(prefix);
@@ -4387,6 +4469,12 @@ public abstract class BatteryStats implements Parcelable {
? "power-save-on" : "power-save-off");
haveModes = true;
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+ pw.print(haveModes ? ", " : " (");
+ pw.print((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+ ? "device-idle-on" : "device-idle-off");
+ haveModes = true;
+ }
if (haveModes) {
pw.print(")");
}
@@ -4396,7 +4484,6 @@ public abstract class BatteryStats implements Parcelable {
return true;
}
- public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_DAILY_ONLY = 1<<2;
public static final int DUMP_HISTORY_ONLY = 1<<3;
@@ -4518,6 +4605,23 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ private void dumpDailyPackageChanges(PrintWriter pw, String prefix,
+ ArrayList<PackageChange> changes) {
+ if (changes == null) {
+ return;
+ }
+ pw.print(prefix); pw.println("Package changes:");
+ for (int i=0; i<changes.size(); i++) {
+ PackageChange pc = changes.get(i);
+ if (pc.mUpdate) {
+ pw.print(prefix); pw.print(" Update "); pw.print(pc.mPackageName);
+ pw.print(" vers="); pw.println(pc.mVersionCode);
+ } else {
+ pw.print(prefix); pw.print(" Uninstall "); pw.println(pc.mPackageName);
+ }
+ }
+ }
+
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
@@ -4528,7 +4632,7 @@ public abstract class BatteryStats implements Parcelable {
prepareForDumpLocked();
final boolean filtering = (flags
- & (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
+ & (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
final long historyTotalSize = getHistoryTotalSize();
@@ -4572,7 +4676,7 @@ public abstract class BatteryStats implements Parcelable {
}
}
- if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+ if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
return;
}
@@ -4648,8 +4752,9 @@ public abstract class BatteryStats implements Parcelable {
int[] outInt = new int[1];
LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
LevelStepTracker csteps = getDailyChargeLevelStepTracker();
- if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0) {
- if ((flags&DUMP_DAILY_ONLY) != 0) {
+ ArrayList<PackageChange> pkgc = getDailyPackageChanges();
+ if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) {
+ if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
if (dumpDurationSteps(pw, " ", " Current daily discharge step durations:",
dsteps, false)) {
dumpDailyLevelStepSummary(pw, " ", "Discharge", dsteps,
@@ -4660,6 +4765,7 @@ public abstract class BatteryStats implements Parcelable {
dumpDailyLevelStepSummary(pw, " ", "Charge", csteps,
sb, outInt);
}
+ dumpDailyPackageChanges(pw, " ", pkgc);
} else {
pw.println(" Current daily steps:");
dumpDailyLevelStepSummary(pw, " ", "Discharge", dsteps,
@@ -4680,7 +4786,7 @@ public abstract class BatteryStats implements Parcelable {
pw.print(" to ");
pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mEndTime).toString());
pw.println(":");
- if ((flags&DUMP_DAILY_ONLY) != 0) {
+ if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
if (dumpDurationSteps(pw, " ",
" Discharge step durations:", dit.mDischargeSteps, false)) {
dumpDailyLevelStepSummary(pw, " ", "Discharge", dit.mDischargeSteps,
@@ -4691,6 +4797,7 @@ public abstract class BatteryStats implements Parcelable {
dumpDailyLevelStepSummary(pw, " ", "Charge", dit.mChargeSteps,
sb, outInt);
}
+ dumpDailyPackageChanges(pw, " ", dit.mPackageChanges);
} else {
dumpDailyLevelStepSummary(pw, " ", "Discharge", dit.mDischargeSteps,
sb, outInt);
@@ -4708,11 +4815,6 @@ public abstract class BatteryStats implements Parcelable {
(flags&DUMP_DEVICE_WIFI_ONLY) != 0);
pw.println();
}
- if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
- pw.println("Statistics since last unplugged:");
- dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
- }
}
@SuppressWarnings("unused")
@@ -4721,12 +4823,12 @@ public abstract class BatteryStats implements Parcelable {
prepareForDumpLocked();
dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA,
- "12", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
+ "13", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
final boolean filtering = (flags &
- (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
+ (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
if (startIteratingHistoryLocked()) {
@@ -4752,7 +4854,7 @@ public abstract class BatteryStats implements Parcelable {
}
}
- if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+ if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
return;
}
@@ -4802,9 +4904,5 @@ public abstract class BatteryStats implements Parcelable {
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1,
(flags&DUMP_DEVICE_WIFI_ONLY) != 0);
}
- if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
- dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
- }
}
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 16dac7d..804d3d0 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -43,6 +43,7 @@ interface IPowerManager
boolean isInteractive();
boolean isPowerSaveMode();
boolean setPowerSaveMode(boolean mode);
+ boolean isDeviceIdleMode();
void reboot(boolean confirm, String reason, boolean wait);
void shutdown(boolean confirm, boolean wait);
@@ -50,6 +51,7 @@ interface IPowerManager
void setStayOnSetting(int val);
void boostScreenBrightness(long time);
+ boolean isScreenBrightnessBoosted();
// temporarily overrides the screen brightness settings to allow the user to
// see the effect of a settings change without applying it immediately
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e303f61..01c9a21 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -364,6 +364,12 @@ public final class PowerManager {
public static final int GO_TO_SLEEP_REASON_HDMI = 5;
/**
+ * Go to sleep reason code: Going to sleep due to the sleep button being pressed.
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_SLEEP_BUTTON = 6;
+
+ /**
* Go to sleep flag: Skip dozing state and directly go to full sleep.
* @hide
*/
@@ -706,6 +712,22 @@ public final class PowerManager {
}
/**
+ * Returns whether the screen brightness is currently boosted to maximum, caused by a call
+ * to {@link #boostScreenBrightness(long)}.
+ * @return {@code True} if the screen brightness is currently boosted. {@code False} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isScreenBrightnessBoosted() {
+ try {
+ return mService.isScreenBrightnessBoosted();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Sets the brightness of the backlights (screen, keyboard, button).
* <p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -850,6 +872,23 @@ public final class PowerManager {
}
/**
+ * Returns true if the device is currently in idle mode. This happens when a device
+ * has been sitting unused and unmoving for a sufficiently long period of time, so that
+ * it decides to go into a lower power-use state. This may involve things like turning
+ * off network access to apps. You can monitor for changes to this state with
+ * {@link #ACTION_DEVICE_IDLE_MODE_CHANGED}.
+ *
+ * @return Returns true if currently in low power mode, else false.
+ */
+ public boolean isDeviceIdleMode() {
+ try {
+ return mService.isDeviceIdleMode();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Turn off the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
@@ -873,6 +912,14 @@ public final class PowerManager {
= "android.os.action.POWER_SAVE_MODE_CHANGED";
/**
+ * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
+ * This broadcast is only sent to registered receivers.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_IDLE_MODE_CHANGED
+ = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+
+ /**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
* This broadcast is only sent to registered receivers.
*
@@ -886,6 +933,16 @@ public final class PowerManager {
public static final String EXTRA_POWER_SAVE_MODE = "mode";
/**
+ * Intent that is broadcast when the state of {@link #isScreenBrightnessBoosted()} has changed.
+ * This broadcast is only sent to registered receivers.
+ *
+ * @hide
+ **/
+ @SystemApi
+ public static final String ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED
+ = "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED";
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 6f31768..00ab262 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -117,7 +117,7 @@ public abstract class PowerManagerInternal {
/**
* Used by the dream manager to override certain properties while dozing.
*
- * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+ * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
* to disable the override.
* @param screenBrightness The overridden screen brightness, or
* {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
@@ -132,4 +132,6 @@ public abstract class PowerManagerInternal {
public interface LowPowerModeListener {
public void onLowPowerModeChanged(boolean enabled);
}
+
+ public abstract void setDeviceIdleMode(boolean enabled);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4834f97..0de9c70 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -631,6 +631,9 @@ public class Process {
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
+ if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) {
+ argsForZygote.add("--enable-jit");
+ }
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 706e0d0..3601a1c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -400,6 +400,18 @@ public class UserManager {
public static final String DISALLOW_WALLPAPER = "no_wallpaper";
/**
+ * Specifies if the user is not allowed to reboot the device into safe boot mode.
+ * This can only be set by device owners and profile owners on the primary user.
+ * The default value is <code>false</code>.
+ *
+ * <p/>Key for user restrictions.
+ * <p/>Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_SAFE_BOOT = "no_safe_boot";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 116110e..6209c2a 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -888,6 +888,21 @@ public interface IMountService extends IInterface {
}
return;
}
+
+ @Override
+ public void waitForAsecScan() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_waitForAsecScan, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return;
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -978,6 +993,8 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_runMaintenance = IBinder.FIRST_CALL_TRANSACTION + 42;
+ static final int TRANSACTION_waitForAsecScan = IBinder.FIRST_CALL_TRANSACTION + 43;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1396,6 +1413,12 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
+ case TRANSACTION_waitForAsecScan: {
+ data.enforceInterface(DESCRIPTOR);
+ waitForAsecScan();
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1680,4 +1703,6 @@ public interface IMountService extends IInterface {
* @throws RemoteException
*/
public void runMaintenance() throws RemoteException;
+
+ public void waitForAsecScan() throws RemoteException;
}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index ccf2cfa..f32e8cf 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -706,8 +706,10 @@ public class Preference implements Comparable<Preference> {
* @param iconResId The icon as a resource ID.
*/
public void setIcon(@DrawableRes int iconResId) {
- mIconResId = iconResId;
- setIcon(mContext.getDrawable(iconResId));
+ if (mIconResId != iconResId) {
+ mIconResId = iconResId;
+ setIcon(mContext.getDrawable(iconResId));
+ }
}
/**
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index e4e753e..44e6410 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -212,7 +212,7 @@ public final class PrintDocumentInfo implements Parcelable {
result = prime * result + mContentType;
result = prime * result + mPageCount;
result = prime * result + (int) mDataSize;
- result = prime * result + (int) mDataSize >> 32;
+ result = prime * result + (int) (mDataSize >> 32);
return result;
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 91a19dc..e4a6f07 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1516,8 +1516,14 @@ public final class ContactsContract {
/**
* Build a {@link #CONTENT_LOOKUP_URI} lookup {@link Uri} using the
* given {@link ContactsContract.Contacts#_ID} and {@link #LOOKUP_KEY}.
+ * <p>
+ * Returns null if unable to construct a valid lookup URI from the
+ * provided parameters.
*/
public static Uri getLookupUri(long contactId, String lookupKey) {
+ if (TextUtils.isEmpty(lookupKey)) {
+ return null;
+ }
return ContentUris.withAppendedId(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
lookupKey), contactId);
}
@@ -4803,6 +4809,14 @@ public final class ContactsContract {
Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities");
/**
+ * The content:// style URI for this table in corp profile
+ *
+ * @hide
+ */
+ public static final Uri CORP_CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities_corp");
+
+ /**
* The content:// style URI for this table, specific to the user's profile.
*/
public static final Uri PROFILE_CONTENT_URI =
@@ -4994,14 +5008,16 @@ public final class ContactsContract {
"phone_lookup");
/**
- * URI used for the "enterprise caller-id".
+ * <p>URI used for the "enterprise caller-id".</p>
*
+ * <p>
* It supports the same semantics as {@link #CONTENT_FILTER_URI} and returns the same
* columns. If the device has no corp profile that is linked to the current profile, it
* behaves in the exact same way as {@link #CONTENT_FILTER_URI}. If there is a corp profile
* linked to the current profile, it first queries against the personal contact database,
* and if no matching contacts are found there, then queries against the
* corp contacts database.
+ * </p>
* <p>
* If a result is from the corp profile, it makes the following changes to the data:
* <ul>
@@ -5984,6 +6000,45 @@ public final class ContactsContract {
"lookup");
/**
+ * <p>URI used for enterprise email lookup.</p>
+ *
+ * <p>
+ * It supports the same semantics as {@link #CONTENT_LOOKUP_URI} and returns the same
+ * columns. If the device has no corp profile that is linked to the current profile, it
+ * behaves in the exact same way as {@link #CONTENT_LOOKUP_URI}. If there is a
+ * corp profile linked to the current profile, it first queries against the personal contact database,
+ * and if no matching contacts are found there, then queries against the
+ * corp contacts database.
+ * </p>
+ * <p>
+ * If a result is from the corp profile, it makes the following changes to the data:
+ * <ul>
+ * <li>
+ * {@link #PHOTO_THUMBNAIL_URI} and {@link #PHOTO_URI} will be rewritten to special
+ * URIs. Use {@link ContentResolver#openAssetFileDescriptor} or its siblings to
+ * load pictures from them.
+ * {@link #PHOTO_ID} and {@link #PHOTO_FILE_ID} will be set to null. Do not
+ * use them.
+ * </li>
+ * <li>
+ * Corp contacts will get artificial {@link #CONTACT_ID}s. In order to tell whether
+ * a contact
+ * is from the corp profile, use
+ * {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
+ * </li>
+ * </ul>
+ * <p>
+ * This URI does NOT support selection nor order-by.
+ *
+ * <pre>
+ * Uri lookupUri = Uri.withAppendedPath(Email.ENTERPRISE_CONTENT_LOOKUP_URI,
+ * Uri.encode(email));
+ * </pre>
+ */
+ public static final Uri ENTERPRISE_CONTENT_LOOKUP_URI =
+ Uri.withAppendedPath(CONTENT_URI, "lookup_enterprise");
+
+ /**
* <p>
* The content:// style URL for email lookup using a filter. The filter returns
* records of MIME type {@link #CONTENT_ITEM_TYPE}. The filter is applied
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3813277..de536bd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5375,6 +5375,7 @@ public final class Settings {
BACKUP_AUTO_RESTORE,
ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_NOTIFICATION_LISTENERS,
+ ENABLED_INPUT_METHODS,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_ENABLED,
ACCESSIBILITY_ENABLED,
@@ -5963,6 +5964,14 @@ public final class Settings {
"hdmi_control_auto_device_off_enabled";
/**
+ * Whether to use the DHCP client from Lollipop and earlier instead of the newer Android DHCP
+ * client.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String LEGACY_DHCP_CLIENT = "legacy_dhcp_client";
+
+ /**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
* @hide
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index ac6bbb7..d24bc13 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -19,6 +19,7 @@ package android.security;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
import android.security.keymaster.OperationResult;
import android.security.KeystoreArguments;
@@ -61,11 +62,12 @@ interface IKeystoreService {
int addRngEntropy(in byte[] data);
int generateKey(String alias, in KeymasterArguments arguments, int uid, int flags,
out KeyCharacteristics characteristics);
- int getKeyCharacteristics(String alias, in byte[] clientId,
- in byte[] appId, out KeyCharacteristics characteristics);
+ int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
+ out KeyCharacteristics characteristics);
int importKey(String alias, in KeymasterArguments arguments, int format,
in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
- ExportResult exportKey(String alias, int format, in byte[] clientId, in byte[] appId);
+ ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
+ in KeymasterBlob appId);
OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
in KeymasterArguments params, out KeymasterArguments operationParams);
OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
new file mode 100644
index 0000000..0b3bf45
--- /dev/null
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Network security policy.
+ *
+ * <p>Network stacks/components should honor this policy to make it possible to centrally control
+ * the relevant aspects of network security behavior.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
+ */
+public class NetworkSecurityPolicy {
+
+ private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy();
+
+ private NetworkSecurityPolicy() {}
+
+ /**
+ * Gets the policy for this process.
+ *
+ * <p>It's fine to cache this reference. Any changes to the policy will be immediately visible
+ * through the reference.
+ */
+ public static NetworkSecurityPolicy getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Returns whether cleartext network traffic (e.g. HTTP, FTP, WebSockets, XMPP, IMAP, SMTP --
+ * 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
+ * encouraged to honor this setting as well.
+ *
+ * <p>This flag is honored on a best effort basis because it's impossible to prevent all
+ * cleartext traffic from Android applications given the level of access provided to them. For
+ * example, there's no expectation that the {@link java.net.Socket} API will honor this flag
+ * 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.
+ */
+ public boolean isCleartextTrafficPermitted() {
+ return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
+ }
+
+ /**
+ * Sets whether cleartext network traffic is permitted for this process.
+ *
+ * <p>This method is used by the platform early on in the application's initialization to set
+ * the policy.
+ *
+ * @hide
+ */
+ public void setCleartextTrafficPermitted(boolean permitted) {
+ libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted);
+ }
+}
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
new file mode 100644
index 0000000..8f70f7c
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keymaster;
+
+/* @hide */
+parcelable KeymasterBlob;
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
new file mode 100644
index 0000000..cb95604
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keymaster;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class KeymasterBlob implements Parcelable {
+ public byte[] blob;
+
+ public KeymasterBlob(byte[] blob) {
+ this.blob = blob;
+ }
+ public static final Parcelable.Creator<KeymasterBlob> CREATOR = new
+ Parcelable.Creator<KeymasterBlob>() {
+ public KeymasterBlob createFromParcel(Parcel in) {
+ return new KeymasterBlob(in);
+ }
+
+ public KeymasterBlob[] newArray(int length) {
+ return new KeymasterBlob[length];
+ }
+ };
+
+ protected KeymasterBlob(Parcel in) {
+ blob = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(blob);
+ }
+}
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index 9af4445..7d587bf 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -26,6 +26,13 @@ class KeymasterBlobArgument extends KeymasterArgument {
public KeymasterBlobArgument(int tag, byte[] blob) {
super(tag);
+ switch (KeymasterDefs.getTagType(tag)) {
+ case KeymasterDefs.KM_BIGNUM:
+ case KeymasterDefs.KM_BYTES:
+ break; // OK.
+ default:
+ throw new IllegalArgumentException("Bad blob tag " + tag);
+ }
this.blob = blob;
}
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 5481e8f..9c03674 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -28,6 +28,12 @@ class KeymasterBooleanArgument extends KeymasterArgument {
public KeymasterBooleanArgument(int tag) {
super(tag);
+ switch (KeymasterDefs.getTagType(tag)) {
+ case KeymasterDefs.KM_BOOL:
+ break; // OK.
+ default:
+ throw new IllegalArgumentException("Bad bool tag " + tag);
+ }
}
public KeymasterBooleanArgument(int tag, Parcel in) {
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index 310f546..bffd24d 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -27,6 +27,12 @@ class KeymasterDateArgument extends KeymasterArgument {
public KeymasterDateArgument(int tag, Date date) {
super(tag);
+ switch (KeymasterDefs.getTagType(tag)) {
+ case KeymasterDefs.KM_DATE:
+ break; // OK.
+ default:
+ throw new IllegalArgumentException("Bad date tag " + tag);
+ }
this.date = date;
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 88cad79..e653b74 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -37,6 +37,7 @@ public final class KeymasterDefs {
public static final int KM_BOOL = 7 << 28;
public static final int KM_BIGNUM = 8 << 28;
public static final int KM_BYTES = 9 << 28;
+ public static final int KM_LONG_REP = 10 << 28;
// Tag values.
public static final int KM_TAG_INVALID = KM_INVALID | 0;
@@ -66,9 +67,10 @@ public final class KeymasterDefs {
public static final int KM_TAG_ALL_USERS = KM_BOOL | 500;
public static final int KM_TAG_USER_ID = KM_INT | 501;
- public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 502;
- public static final int KM_TAG_USER_AUTH_ID = KM_INT_REP | 503;
- public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 504;
+ public static final int KM_TAG_USER_SECURE_ID = KM_LONG_REP | 502;
+ public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
+ public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
+ public static final int KM_TAG_AUTH_TIMEOUT = KM_INT | 505;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -82,6 +84,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
public static final int KM_TAG_CHUNK_LENGTH = KM_INT | 1002;
+ public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = 1;
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index c3738d7..da81715 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -26,6 +26,15 @@ class KeymasterIntArgument extends KeymasterArgument {
public KeymasterIntArgument(int tag, int value) {
super(tag);
+ switch (KeymasterDefs.getTagType(tag)) {
+ case KeymasterDefs.KM_INT:
+ case KeymasterDefs.KM_INT_REP:
+ case KeymasterDefs.KM_ENUM:
+ case KeymasterDefs.KM_ENUM_REP:
+ break; // OK.
+ default:
+ throw new IllegalArgumentException("Bad int tag " + tag);
+ }
this.value = value;
}
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index 3c565b8..9d2be09 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -26,6 +26,12 @@ class KeymasterLongArgument extends KeymasterArgument {
public KeymasterLongArgument(int tag, long value) {
super(tag);
+ switch (KeymasterDefs.getTagType(tag)) {
+ case KeymasterDefs.KM_LONG:
+ break; // OK.
+ default:
+ throw new IllegalArgumentException("Bad long tag " + tag);
+ }
this.value = value;
}
diff --git a/core/java/android/service/fingerprint/Fingerprint.aidl b/core/java/android/service/fingerprint/Fingerprint.aidl
new file mode 100644
index 0000000..c9fd989
--- /dev/null
+++ b/core/java/android/service/fingerprint/Fingerprint.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.service.fingerprint;
+
+// @hide
+parcelable Fingerprint;
diff --git a/core/java/android/service/fingerprint/Fingerprint.java b/core/java/android/service/fingerprint/Fingerprint.java
new file mode 100644
index 0000000..37552eb
--- /dev/null
+++ b/core/java/android/service/fingerprint/Fingerprint.java
@@ -0,0 +1,93 @@
+/*
+ * 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.service.fingerprint;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for fingerprint metadata.
+ * @hide
+ */
+public final class Fingerprint implements Parcelable {
+ private CharSequence mName;
+ private int mGroupId;
+ private int mFingerId;
+ private long mDeviceId; // physical device this is associated with
+
+ public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
+ mName = name;
+ mGroupId = groupId;
+ mFingerId = fingerId;
+ mDeviceId = deviceId;
+ }
+
+ private Fingerprint(Parcel in) {
+ mName = in.readString();
+ mGroupId = in.readInt();
+ mFingerId = in.readInt();
+ mDeviceId = in.readLong();
+ }
+
+ /**
+ * Gets the human-readable name for the given fingerprint.
+ * @return name given to finger
+ */
+ public CharSequence getName() { return mName; }
+
+ /**
+ * Gets the device-specific finger id. Used by Settings to map a name to a specific
+ * fingerprint template.
+ * @return device-specific id for this finger
+ * @hide
+ */
+ public int getFingerId() { return mFingerId; }
+
+ /**
+ * Gets the group id specified when the fingerprint was enrolled.
+ * @return group id for the set of fingerprints this one belongs to.
+ * @hide
+ */
+ public int getGroupId() { return mGroupId; }
+
+ /**
+ * Device this fingerprint belongs to.
+ * @hide
+ */
+ public long getDeviceId() { return mDeviceId; }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mName.toString());
+ out.writeInt(mGroupId);
+ out.writeInt(mFingerId);
+ out.writeLong(mDeviceId);
+ }
+
+ public static final Parcelable.Creator<Fingerprint> CREATOR
+ = new Parcelable.Creator<Fingerprint>() {
+ public Fingerprint createFromParcel(Parcel in) {
+ return new Fingerprint(in);
+ }
+
+ public Fingerprint[] newArray(int size) {
+ return new Fingerprint[size];
+ }
+ };
+}; \ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index 6375668..bb90e40 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -20,17 +20,25 @@ import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Binder;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.fingerprint.FingerprintManager.EnrollmentCallback;
import android.util.Log;
import android.util.Slog;
+import java.security.Signature;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import javax.crypto.Cipher;
+
/**
* A class that coordinates access to the fingerprint hardware.
* @hide
@@ -45,9 +53,6 @@ public class FingerprintManager {
private static final int MSG_ERROR = 103;
private static final int MSG_REMOVED = 104;
- // Errors generated by layers above HAL
- public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
-
// Message types. Must agree with HAL (fingerprint.h)
public static final int FINGERPRINT_ERROR = -1;
public static final int FINGERPRINT_ACQUIRED = 1;
@@ -60,254 +65,499 @@ public class FingerprintManager {
public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+ public static final int FINGERPRINT_ERROR_CANCELED = 5;
+ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
- // FINGERPRINT_ACQUIRED messages. Must agree with HAL (fingerprint.h)
+ // Image acquisition messages. Must agree with HAL (fingerprint.h)
public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
private IFingerprintService mService;
- private FingerprintManagerReceiver mClientReceiver;
private Context mContext;
private IBinder mToken = new Binder();
+ private AuthenticationCallback mAuthenticationCallback;
+ private EnrollmentCallback mEnrollmentCallback;
+ private RemovalCallback mRemovalCallback;
+ private CryptoObject mCryptoObject;
+ private Fingerprint mRemovalFingerprint;
+ private boolean mListening;
- private Handler mHandler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- if (mClientReceiver != null) {
- switch(msg.what) {
- case MSG_ENROLL_RESULT:
- mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
- break;
- case MSG_ACQUIRED:
- mClientReceiver.onAcquired(msg.arg1);
- break;
- case MSG_PROCESSED:
- mClientReceiver.onProcessed(msg.arg1);
- break;
- case MSG_ERROR:
- mClientReceiver.onError(msg.arg1);
- break;
- case MSG_REMOVED:
- mClientReceiver.onRemoved(msg.arg1);
- }
- }
- }
+ /**
+ * A wrapper class for a limited number of crypto objects supported by FingerprintManager.
+ */
+ public static class CryptoObject {
+ CryptoObject(Signature signature) { mSignature = signature; }
+ CryptoObject(Cipher cipher) { mCipher = cipher; }
+ private Signature mSignature;
+ private Cipher mCipher;
};
- public static final class FingerprintItem {
- public CharSequence name;
- public int id;
- FingerprintItem(CharSequence name, int id) {
- this.name = name;
- this.id = id;
+ /**
+ * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
+ * AuthenticationCallback, CancellationSignal, int)}
+ */
+ public static final class AuthenticationResult {
+ private Fingerprint mFingerprint;
+ private CryptoObject mCryptoObject;
+
+ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
+ mCryptoObject = crypto;
+ mFingerprint = fingerprint;
}
- }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
+ * AuthenticationCallback, CancellationSignal, int)}
+ */
+ public CryptoObject getCryptoObject() { return mCryptoObject; }
+
+ /**
+ * Obtain the Fingerprint associated with this operation. Applications are discouraged
+ * from associating specific fingers with specific applications or operations. Hence this
+ * is not public.
+ * @hide
+ */
+ public Fingerprint getFingerprint() { return mFingerprint; }
+ };
/**
- * @hide
+ * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
+ * AuthenticationCallback, CancellationSignal, int)}. Users of {@link #FingerprintManager()}
+ * must provide an implementation of this to {@link FingerprintManager#authenticate(
+ * CryptoObject, AuthenticationCallback, CancellationSignal, int) for listening to fingerprint
+ * events.
*/
- public FingerprintManager(Context context, IFingerprintService service) {
- mContext = context;
- mService = service;
- if (mService == null) {
- Slog.v(TAG, "FingerprintManagerService was null");
- }
- }
+ public static abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ * @param errMsgId an integer identifying the error message.
+ * @param errString a human-readible error string that can be shown in UI.
+ */
+ public abstract void onAuthenticationError(int errMsgId, CharSequence errString);
- private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Sensor dirty, please clean it."
+ * @param helpMsgId an integer identifying the error message.
+ * @param helpString a human-readible string that can be shown in UI.
+ */
+ public abstract void onAuthenticationHelp(int helpMsgId, CharSequence helpString);
- public void onEnrollResult(int fingerprintId, int remaining) {
- mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
- }
+ /**
+ * Called when a fingerprint is recognized.
+ * @param result an object containing authentication-related data.
+ */
+ public abstract void onAuthenticationSucceeded(AuthenticationResult result);
+ };
- public void onAcquired(int acquireInfo) {
- mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
- }
+ /**
+ * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
+ * CancellationSignal, int). Users of {@link #FingerprintManager()}
+ * must provide an implementation of this to {@link FingerprintManager#enroll(long,
+ * EnrollmentCallback, CancellationSignal, int) for listening to fingerprint events.
+ */
+ public static abstract class EnrollmentCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ * @param errMsgId an integer identifying the error message.
+ * @param errString a human-readible error string that can be shown in UI.
+ */
+ public abstract void onEnrollmentError(int errMsgId, CharSequence errString);
- public void onProcessed(int fingerprintId) {
- mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
- }
+ /**
+ * Called when a recoverable error has been encountered during enrollment. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Sensor dirty, please clean it" or what they need to do next, such as
+ * "Touch sensor again."
+ * @param helpMsgId an integer identifying the error message.
+ * @param helpString a human-readible string that can be shown in UI.
+ */
+ public abstract void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
- public void onError(int error) {
- mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
- }
+ /**
+ * Called as each enrollment step progresses. Enrollment is considered complete when
+ * remaining reaches 0. This function will not be called if enrollment fails. See
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+ * @param remaining the number of remaining steps.
+ */
+ public abstract void onEnrollmentProgress(int remaining);
+ };
- public void onRemoved(int fingerprintId) {
- mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
- }
+ /**
+ * Callback structure provided to {@link FingerprintManager#remove(int). Users of
+ * {@link #FingerprintManager()} may optionally provide an implementation of this to
+ * {@link FingerprintManager#remove(int, int, RemovalCallback)} for listening to
+ * fingerprint template removal events.
+ */
+ public static abstract class RemovalCallback {
+ /**
+ * Called when the given fingerprint can't be removed.
+ * @param fp the fingerprint that the call attempted to remove.
+ * @param errMsgId an associated error message id.
+ * @param errString an error message indicating why the fingerprint id can't be removed.
+ */
+ public abstract void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString);
+
+ /**
+ * Called when a given fingerprint is successfully removed.
+ * @param fingerprint the fingerprint template that was removed.
+ */
+ public abstract void onRemovalSucceeded(Fingerprint fingerprint);
};
/**
- * Determine whether the user has at least one fingerprint enrolled and enabled.
+ * Request authentication of a crypto object. This call warms up the fingerprint hardware
+ * and starts scanning for a fingerprint. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
*
- * @return true if at least one is enrolled and enabled
+ * @param crypto object associated with the call or null if none required.
+ * @param callback an object to receive authentication events
+ * @param cancel an object that can be used to cancel authentication
+ * @param flags optional flags
*/
- public boolean enrolledAndEnabled() {
- ContentResolver res = mContext.getContentResolver();
- return Settings.Secure.getInt(res, "fingerprint_enabled", 0) != 0
- && FingerprintUtils.getFingerprintIdsForUser(res, getCurrentUserId()).length > 0;
+ public void authenticate(CryptoObject crypto, AuthenticationCallback callback,
+ CancellationSignal cancel, int flags) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an authentication callback");
+ }
+
+ // TODO: handle cancel
+
+ if (mService != null) try {
+ mAuthenticationCallback = callback;
+ mCryptoObject = crypto;
+ long sessionId = 0; // TODO: get from crypto object
+ startListening();
+ mService.authenticate(mToken, sessionId, getCurrentUserId(), flags);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception while authenticating: ", e);
+ stopListening();
+ }
}
/**
- * Start the enrollment process. Timeout dictates how long to wait for the user to
- * enroll a fingerprint.
- *
- * @param timeout
+ * Request fingerprint enrollment. This call warms up the fingerprint hardware
+ * and starts scanning for fingerprints. Progress will be indicated by callbacks to the
+ * {@link EnrollmentCallback} object. It terminates when
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+ * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ * @param challenge a unique id provided by a recent verification of device credentials
+ * (e.g. pin, pattern or password).
+ * @param callback an object to receive enrollment events
+ * @param cancel an object that can be used to cancel enrollment
+ * @param flags optional flags
*/
- public void enroll(long timeout) {
- if (mServiceReceiver == null) {
- sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
- return;
+ public void enroll(long challenge, EnrollmentCallback callback,
+ CancellationSignal cancel, int flags) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an enrollment callback");
}
+
+ // TODO: handle cancel
+
if (mService != null) try {
- mService.enroll(mToken, timeout, getCurrentUserId());
+ mEnrollmentCallback = callback;
+ startListening();
+ mService.enroll(mToken, getCurrentUserId(), flags);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception while enrolling: ", e);
- sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ Log.v(TAG, "Remote exception in enroll: ", e);
+ stopListening();
}
}
/**
- * Remove the given fingerprintId from the system. FingerprintId of 0 has special meaning
- * which is to delete all fingerprint data for the current user. Use with caution.
- * @param fingerprintId
+ * Remove given fingerprint template from fingerprint hardware and/or protected storage.
+ * @param fp the fingerprint item to remove
+ * @param callback an optional callback to verify that fingerprint templates have been
+ * successfully removed. May be null of no callback is required.
+ * @hide
*/
- public void remove(int fingerprintId) {
- if (mServiceReceiver == null) {
- sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
- return;
- }
- if (mService != null) {
- try {
- mService.remove(mToken, fingerprintId, getCurrentUserId());
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
- }
- } else {
- Log.w(TAG, "remove(): Service not connected!");
- sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ public void remove(Fingerprint fp, RemovalCallback callback) {
+ if (mService != null) try {
+ mRemovalCallback = callback;
+ mRemovalFingerprint = fp;
+ startListening();
+ mService.remove(mToken, fp.getFingerId(), getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote in remove: ", e);
+ stopListening();
}
}
/**
- * Starts listening for fingerprint events. When a finger is scanned or recognized, the
- * client will be notified via the callback.
+ * Renames the given fingerprint template
+ * @param fpId the fingerprint id
+ * @param newName the new name
+ * @hide
*/
- public void startListening(FingerprintManagerReceiver receiver) {
- mClientReceiver = receiver;
+ public void rename(int fpId, String newName) {
+ // Renames the given fpId
if (mService != null) {
try {
- mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
+ mService.rename(fpId, getCurrentUserId(), newName);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in startListening(): ", e);
+ Log.v(TAG, "Remote exception in rename(): ", e);
}
} else {
- Log.w(TAG, "startListening(): Service not connected!");
- sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ Log.w(TAG, "rename(): Service not connected!");
}
}
- private int getCurrentUserId() {
- try {
- return ActivityManagerNative.getDefault().getCurrentUser().id;
+ /**
+ * Obtain the list of enrolled fingerprints templates.
+ * @return list of current fingerprint items
+ */
+ public List<Fingerprint> getEnrolledFingerprints() {
+ if (mService != null) try {
+ return mService.getEnrolledFingerprints(getCurrentUserId());
} catch (RemoteException e) {
- Log.w(TAG, "Failed to get current user id\n");
- return UserHandle.USER_NULL;
+ Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
}
+ return null;
}
/**
- * Stops the client from listening to fingerprint events.
+ * Determine if fingerprint hardware is present and functional.
+ * @return true if hardware is present and functional, false otherwise.
+ * @hide
*/
- public void stopListening() {
+ public boolean isHardwareDetected() {
if (mService != null) {
try {
- mService.stopListening(mToken, getCurrentUserId());
- mClientReceiver = null;
+ long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+ return mService.isHardwareDetected(deviceId);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in stopListening(): ", e);
+ Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
}
} else {
- Log.w(TAG, "stopListening(): Service not connected!");
- sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
}
+ return false;
}
- public void enrollCancel() {
- if (mServiceReceiver == null) {
- sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
- return;
+ private Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch(msg.what) {
+ case MSG_ENROLL_RESULT:
+ sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
+ break;
+ case MSG_ACQUIRED:
+ sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
+ break;
+ case MSG_PROCESSED:
+ sendProcessedResult((Fingerprint) msg.obj);
+ break;
+ case MSG_ERROR:
+ sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
+ break;
+ case MSG_REMOVED:
+ sendRemovedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
+ msg.arg2 /* groupId */);
+ }
}
- if (mService != null) {
- try {
- mService.enrollCancel(mToken, getCurrentUserId());
- mClientReceiver = null;
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception in enrollCancel(): ", e);
- sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+
+ private void sendRemovedResult(long deviceId, int fingerId, int groupId) {
+ if (mRemovalCallback != null) {
+ int reqFingerId = mRemovalFingerprint.getFingerId();
+ int reqGroupId = mRemovalFingerprint.getGroupId();
+ if (fingerId != reqFingerId) {
+ Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
+ }
+ if (fingerId != reqFingerId) {
+ Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
+ }
+ mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
}
- } else {
- Log.w(TAG, "enrollCancel(): Service not connected!");
}
- }
- private void sendError(int msg, int arg1, int arg2) {
- mHandler.obtainMessage(msg, arg1, arg2);
- }
+ private void sendErrorResult(long deviceId, int errMsgId) {
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentError(errMsgId, getErrorString(errMsgId));
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationError(errMsgId, getErrorString(errMsgId));
+ } else if (mRemovalCallback != null) {
+ mRemovalCallback.onRemovalError(mRemovalFingerprint, errMsgId,
+ getErrorString(errMsgId));
+ }
+ }
+
+ private void sendEnrollResult(Fingerprint fp, int remaining) {
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentProgress(remaining);
+ }
+ }
+
+ private void sendProcessedResult(Fingerprint fp) {
+ if (mAuthenticationCallback != null) {
+ AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
+ }
+
+ private void sendAcquiredResult(long deviceId, int acquireInfo) {
+ final String msg = getAcquiredString(acquireInfo);
+ if (msg == null) return;
+
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentHelp(acquireInfo, msg);
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationHelp(acquireInfo, msg);
+ }
+ }
+
+ private String getErrorString(int errMsg) {
+ switch (errMsg) {
+ case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_unable_to_process);
+ case FINGERPRINT_ERROR_HW_UNAVAILABLE:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_available);
+ case FINGERPRINT_ERROR_NO_SPACE:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_space);
+ case FINGERPRINT_ERROR_TIMEOUT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_timeout);
+ default:
+ if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
+ int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.fingerprint_error_vendor);
+ if (msgNumber < msgArray.length) {
+ return msgArray[msgNumber];
+ }
+ }
+ return null;
+ }
+ }
+
+ private String getAcquiredString(int acquireInfo) {
+ switch (acquireInfo) {
+ case FINGERPRINT_ACQUIRED_GOOD:
+ return null;
+ case FINGERPRINT_ACQUIRED_PARTIAL:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_partial);
+ case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_insufficient);
+ case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_imager_dirty);
+ case FINGERPRINT_ACQUIRED_TOO_SLOW:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_too_slow);
+ case FINGERPRINT_ACQUIRED_TOO_FAST:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_too_fast);
+ default:
+ if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) {
+ int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE;
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.fingerprint_acquired_vendor);
+ if (msgNumber < msgArray.length) {
+ return msgArray[msgNumber];
+ }
+ }
+ return null;
+ }
+ }
+ };
/**
- * @return list of current fingerprint items
* @hide
*/
- public List<FingerprintItem> getEnrolledFingerprints() {
- int[] ids = FingerprintUtils.getFingerprintIdsForUser(mContext.getContentResolver(),
- getCurrentUserId());
- List<FingerprintItem> result = new ArrayList<FingerprintItem>();
- for (int i = 0; i < ids.length; i++) {
- // TODO: persist names in Settings
- FingerprintItem item = new FingerprintItem("Finger" + ids[i], ids[i]);
- result.add(item);
+ public FingerprintManager(Context context, IFingerprintService service) {
+ mContext = context;
+ mService = service;
+ if (mService == null) {
+ Slog.v(TAG, "FingerprintManagerService was null");
+ }
+ }
+
+ private int getCurrentUserId() {
+ try {
+ return ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get current user id\n");
+ return UserHandle.USER_NULL;
}
- return result;
}
/**
- * Determine if fingerprint hardware is present and functional.
- * @return true if hardware is present and functional, false otherwise.
- * @hide
+ * Stops the client from listening to fingerprint events.
*/
- public boolean isHardwareDetected() {
+ private void stopListening() {
if (mService != null) {
try {
- return mService.isHardwareDetected();
+ if (mListening) {
+ mService.removeListener(mToken, mServiceReceiver);
+ mListening = false;
+ }
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
+ Log.v(TAG, "Remote exception in stopListening(): ", e);
}
} else {
- Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
+ Log.w(TAG, "stopListening(): Service not connected!");
}
- return false;
}
/**
- * Renames the given fingerprint template
- * @param fpId the fingerprint id
- * @param newName the new name
- * @hide
+ * Starts listening for fingerprint events for this client.
*/
- public void rename(int fpId, String newName) {
- // Renames the given fpId
+ private void startListening() {
if (mService != null) {
try {
- mService.rename(fpId, newName);
+ if (!mListening) {
+ mService.addListener(mToken, mServiceReceiver, getCurrentUserId());
+ mListening = true;
+ }
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in rename(): ", e);
+ Log.v(TAG, "Remote exception in startListening(): ", e);
}
} else {
- Log.w(TAG, "rename(): Service not connected!");
+ Log.w(TAG, "startListening(): Service not connected!");
}
}
+
+ private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+ new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+ }
+
+ public void onAcquired(long deviceId, int acquireInfo) {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
+ }
+
+ public void onProcessed(long deviceId, int fingerId, int groupId) {
+ mHandler.obtainMessage(MSG_PROCESSED,
+ new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+ }
+
+ public void onError(long deviceId, int error) {
+ mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
+ }
+
+ public void onRemoved(long deviceId, int fingerId, int groupId) {
+ mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
+ }
+ };
+
} \ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
deleted file mode 100644
index 85677ba..0000000
--- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package android.service.fingerprint;
-/**
- * Copyright (C) 2014 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.
- */
-
-/**
- * @hide
- */
-public class FingerprintManagerReceiver {
- /**
- * Fingerprint enrollment progress update. Enrollment is considered complete if
- * remaining hits 0 without {@link #onError(int)} being called.
- *
- * @param fingerprintId the fingerprint we're currently enrolling
- * @param remaining the number of samples required to complete enrollment. It's up to
- * the hardware to define what each step in enrollment means. Some hardware
- * requires multiple samples of the same part of the finger. Others require sampling of
- * different parts of the finger. The enrollment flow can use remaining to
- * mean "step x" of the process or "just need another sample."
- */
- public void onEnrollResult(int fingerprintId, int remaining) { }
-
- /**
- * Fingerprint touch detected, but not processed yet. Clients will use this message to
- * determine a good or bad scan before the fingerprint is processed. This is meant for the
- * client to provide feedback about the scan or alert the user that recognition is to follow.
- *
- * @param acquiredInfo one of:
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD},
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL},
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT},
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY},
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW},
- * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST}
- */
- public void onAcquired(int acquiredInfo) { }
-
- /**
- * Fingerprint has been detected and processed. A non-zero return indicates a valid
- * fingerprint was detected.
- *
- * @param fingerprintId the finger id, or 0 if not recognized.
- */
- public void onProcessed(int fingerprintId) { }
-
- /**
- * An error was detected during scan or enrollment. One of
- * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
- * {@link FingerprintManager#FINGERPRINT_ERROR_UNABLE_TO_PROCESS} or
- * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
- * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
- *
- * @param error one of the above error codes
- */
- public void onError(int error) { }
-
- /**
- * The given fingerprint template was successfully removed by the driver.
- * See {@link FingerprintManager#remove(int)}
- *
- * @param fingerprintId id of template to remove.
- */
- public void onRemoved(int fingerprintId) { }
-} \ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
index cc17b99..62acbb9 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -67,7 +67,7 @@ class FingerprintUtils {
return toIntArray(tmp);
}
- public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+ public static void addFingerprintIdForUser(ContentResolver res, int fingerId, int userId) {
// FingerId 0 has special meaning.
if (fingerId == 0) {
Log.w(TAG, "Tried to add fingerId 0");
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
index 9b4750b..e5d3ad4 100644
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -17,31 +17,42 @@ package android.service.fingerprint;
import android.os.Bundle;
import android.service.fingerprint.IFingerprintServiceReceiver;
+import android.service.fingerprint.Fingerprint;
+import java.util.List;
/**
* Communication channel from client to the fingerprint service.
* @hide
*/
interface IFingerprintService {
- // Any errors resulting from this call will be returned to the listener
- void enroll(IBinder token, long timeout, int userId);
+ // Authenticate the given sessionId with a fingerprint
+ void authenticate(IBinder token, long sessionId, int groupId, int flags);
- // Any errors resulting from this call will be returned to the listener
- void enrollCancel(IBinder token, int userId);
+ // Start fingerprint enrollment
+ void enroll(IBinder token, int groupId, int flags);
// Any errors resulting from this call will be returned to the listener
- void remove(IBinder token, int fingerprintId, int userId);
+ void remove(IBinder token, int fingerId, int groupId);
+
+ // Rename the fingerprint specified by fingerId and groupId to the given name
+ void rename(int fingerId, int groupId, String name);
- // Start listening for fingerprint events. This has the side effect of starting
- // the hardware if not already started.
- void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
+ // Get a list of enrolled fingerprints in the given group.
+ List<Fingerprint> getEnrolledFingerprints(int groupId);
- // Stops listening for fingerprints
- void stopListening(IBinder token, int userId);
+ // Register listener for an instance of FingerprintManager
+ void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId);
+
+ // Unregister listener for an instance of FingerprintManager
+ void removeListener(IBinder token, IFingerprintServiceReceiver receiver);
// Determine if HAL is loaded and ready
- boolean isHardwareDetected();
+ boolean isHardwareDetected(long deviceId);
+
+ // Gets the number of hardware devices
+ // int getHardwareDeviceCount();
+
+ // Gets the unique device id for hardware enumerated at i
+ // long getHardwareDevice(int i);
- // Rename the given fingerprint id
- void rename(int fpId, String name);
}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
index af4128f..f025064 100644
--- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -23,9 +23,9 @@ import android.os.UserHandle;
* @hide
*/
oneway interface IFingerprintServiceReceiver {
- void onEnrollResult(int fingerprintId, int remaining);
- void onAcquired(int acquiredInfo);
- void onProcessed(int fingerprintId);
- void onError(int error);
- void onRemoved(int fingerprintId);
+ void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
+ void onAcquired(long deviceId, int acquiredInfo);
+ void onProcessed(long deviceId, int fingerId, int groupId);
+ void onError(long deviceId, int error);
+ void onRemoved(long deviceId, int fingerId, int groupId);
}
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 797457a..4f4b2d5 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -17,6 +17,7 @@
package android.service.voice;
import android.content.Intent;
+import android.graphics.Bitmap;
import android.os.Bundle;
/**
@@ -26,6 +27,7 @@ oneway interface IVoiceInteractionSession {
void show(in Bundle sessionArgs, int flags);
void hide();
void handleAssist(in Bundle assistData);
+ void handleScreenshot(in Bitmap screenshot);
void taskStarted(in Intent intent, int taskId);
void taskFinished(in Intent intent, int taskId);
void closeSystemDialogs();
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0c01b25..419b92b 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -71,11 +71,17 @@ public class VoiceInteractionService extends Service {
public static final String SERVICE_META_DATA = "android.voice_interaction";
/**
- * Flag for use with {@link #showSession: request that the session be started with
+ * Flag for use with {@link #showSession}: request that the session be started with
* assist data from the currently focused activity.
*/
public static final int START_WITH_ASSIST = 1<<0;
+ /**
+ * Flag for use with {@link #showSession}: request that the session be started with
+ * a screen shot of the currently focused activity.
+ */
+ public static final int START_WITH_SCREENSHOT = 1<<1;
+
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
@Override public void ready() {
mHandler.sendEmptyMessage(MSG_READY);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 11eaa06..7a5bb90 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -22,6 +22,7 @@ import android.app.VoiceInteractor;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.inputmethodservice.SoftInputWindow;
@@ -179,6 +180,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
}
@Override
+ public void handleScreenshot(Bitmap screenshot) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
+ screenshot));
+ }
+
+ @Override
public void taskStarted(Intent intent, int taskId) {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
taskId, intent));
@@ -323,8 +330,9 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
static final int MSG_DESTROY = 103;
static final int MSG_HANDLE_ASSIST = 104;
- static final int MSG_SHOW = 105;
- static final int MSG_HIDE = 106;
+ static final int MSG_HANDLE_SCREENSHOT = 105;
+ static final int MSG_SHOW = 106;
+ static final int MSG_HIDE = 107;
class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
@Override
@@ -396,9 +404,13 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
doDestroy();
break;
case MSG_HANDLE_ASSIST:
- if (DEBUG) Log.d(TAG, "onHandleAssist: " + (Bundle)msg.obj);
+ if (DEBUG) Log.d(TAG, "onHandleAssist: " + msg.obj);
onHandleAssist((Bundle) msg.obj);
break;
+ case MSG_HANDLE_SCREENSHOT:
+ if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
+ onHandleScreenshot((Bitmap) msg.obj);
+ break;
case MSG_SHOW:
if (DEBUG) Log.d(TAG, "doShow: args=" + msg.obj
+ " flags=" + msg.arg1);
@@ -768,6 +780,9 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
public void onHandleAssist(Bundle assistBundle) {
}
+ public void onHandleScreenshot(Bitmap screenshot) {
+ }
+
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4902a71..1674950 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,15 +17,12 @@
package android.service.wallpaper;
import android.content.res.TypedArray;
-import android.os.Build;
import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
@@ -158,7 +155,6 @@ public abstract class WallpaperService extends Service {
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
int mCurWindowPrivateFlags = mWindowPrivateFlags;
- TypedValue mOutsetBottom;
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
@@ -171,8 +167,6 @@ public abstract class WallpaperService extends Service {
final Rect mFinalStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
- private boolean mIsEmulator;
- private boolean mIsCircularEmulator;
private boolean mWindowIsRound;
final WindowManager.LayoutParams mLayout
@@ -629,31 +623,12 @@ public abstract class WallpaperService extends Service {
mLayout.token = mWindowToken;
if (!mCreated) {
- // Retrieve watch round and outset info
- final WindowManager windowService = (WindowManager)getSystemService(
- Context.WINDOW_SERVICE);
+ // Retrieve watch round info
TypedArray windowStyle = obtainStyledAttributes(
com.android.internal.R.styleable.Window);
- final Display display = windowService.getDefaultDisplay();
- final boolean shouldUseBottomOutset =
- display.getDisplayId() == Display.DEFAULT_DISPLAY;
- if (shouldUseBottomOutset && windowStyle.hasValue(
- R.styleable.Window_windowOutsetBottom)) {
- if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
- windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
- mOutsetBottom);
- } else {
- mOutsetBottom = null;
- }
- mWindowIsRound = getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
+ mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources());
windowStyle.recycle();
- // detect emulator
- mIsEmulator = Build.HARDWARE.contains("goldfish");
- mIsCircularEmulator = SystemProperties.getBoolean(
- ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
-
// Add window
mLayout.type = mIWallpaperEngine.mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
@@ -783,18 +758,11 @@ public abstract class WallpaperService extends Service {
mDispatchedOverscanInsets.set(mOverscanInsets);
mDispatchedContentInsets.set(mContentInsets);
mDispatchedStableInsets.set(mStableInsets);
- final boolean isRound = (mIsEmulator && mIsCircularEmulator)
- || mWindowIsRound;
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
- if (mOutsetBottom != null) {
- final DisplayMetrics metrics = getResources().getDisplayMetrics();
- mFinalSystemInsets.bottom =
- ( (int) mOutsetBottom.getDimension(metrics) )
- + mIWallpaperEngine.mDisplayPadding.bottom;
- }
+ mFinalSystemInsets.bottom = mIWallpaperEngine.mDisplayPadding.bottom;
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets, isRound);
+ null, mFinalStableInsets, mWindowIsRound);
onApplyWindowInsets(insets);
}
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index dc93bc2..7bebbfb 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -244,13 +244,18 @@ public class Html {
next++;
}
- withinParagraph(out, text, i, next - nl, nl, next == end);
+ if (withinParagraph(out, text, i, next - nl, nl, next == end)) {
+ /* Paragraph should be closed */
+ out.append("</p>\n");
+ out.append(getOpenParaTagWithDirection(text, next, end));
+ }
}
out.append("</p>\n");
}
- private static void withinParagraph(StringBuilder out, Spanned text,
+ /* Returns true if the caller should close and reopen the paragraph. */
+ private static boolean withinParagraph(StringBuilder out, Spanned text,
int start, int end, int nl,
boolean last) {
int next;
@@ -363,17 +368,14 @@ public class Html {
}
}
- String p = last ? "" : "</p>\n" + getOpenParaTagWithDirection(text, start, end);
-
if (nl == 1) {
out.append("<br>\n");
- } else if (nl == 2) {
- out.append(p);
+ return false;
} else {
for (int i = 2; i < nl; i++) {
out.append("<br>");
}
- out.append(p);
+ return !last;
}
}
@@ -672,7 +674,7 @@ class HtmlToSpannedConverter implements ContentHandler {
String name = f.mColor.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes);
+ ColorStateList colors = res.getColorStateList(colorRes, null);
text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index fcf1828..928bf16 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1153,7 +1153,10 @@ public abstract class Layout {
return end - 1;
}
- if (ch != ' ' && ch != '\t') {
+ // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+ if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
+ (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
+ ch == 0x205F || ch == 0x3000)) {
break;
}
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 7ce44e1..992dc4d 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1006,28 +1006,43 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
return new String(buf);
}
+ /**
+ * Returns the depth of TextWatcher callbacks. Returns 0 when the object is not handling
+ * TextWatchers. A return value greater than 1 implies that a TextWatcher caused a change that
+ * recursively triggered a TextWatcher.
+ */
+ public int getTextWatcherDepth() {
+ return mTextWatcherDepth;
+ }
+
private void sendBeforeTextChanged(TextWatcher[] watchers, int start, int before, int after) {
int n = watchers.length;
+ mTextWatcherDepth++;
for (int i = 0; i < n; i++) {
watchers[i].beforeTextChanged(this, start, before, after);
}
+ mTextWatcherDepth--;
}
private void sendTextChanged(TextWatcher[] watchers, int start, int before, int after) {
int n = watchers.length;
+ mTextWatcherDepth++;
for (int i = 0; i < n; i++) {
watchers[i].onTextChanged(this, start, before, after);
}
+ mTextWatcherDepth--;
}
private void sendAfterTextChanged(TextWatcher[] watchers) {
int n = watchers.length;
+ mTextWatcherDepth++;
for (int i = 0; i < n; i++) {
watchers[i].afterTextChanged(this);
}
+ mTextWatcherDepth--;
}
private void sendSpanAdded(Object what, int start, int end) {
@@ -1524,6 +1539,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
private IdentityHashMap<Object, Integer> mIndexOfSpan;
private int mLowWaterMark; // indices below this have not been touched
+ // TextWatcher callbacks may trigger changes that trigger more callbacks. This keeps track of
+ // how deep the callbacks go.
+ private int mTextWatcherDepth;
+
// TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned}
private static final int MARK = 1;
private static final int POINT = 2;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ee39e27..b47418f 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -170,8 +170,9 @@ public class StaticLayout extends Layout {
* Measurement and break iteration is done in native code. The protocol for using
* the native code is as follows.
*
- * For each paragraph, do a nSetText of the paragraph text. Then, for each run within the
- * paragraph:
+ * For each paragraph, do a nSetText of the paragraph text. Also do nSetLineWidth.
+ *
+ * Then, for each run within the paragraph:
* - setLocale (this must be done at least for the first run, optional afterwards)
* - one of the following, depending on the type of run:
* + addStyleRun (a text run, to be measured in native code)
@@ -459,7 +460,26 @@ public class StaticLayout extends Layout {
byte[] chdirs = measured.mLevels;
int dir = measured.mDir;
boolean easy = measured.mEasy;
- nSetText(b.mNativePtr, chs, paraEnd - paraStart);
+
+ // tab stop locations
+ int[] variableTabStops = null;
+ if (spanned != null) {
+ TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+ paraEnd, TabStopSpan.class);
+ if (spans.length > 0) {
+ int[] stops = new int[spans.length];
+ for (int i = 0; i < spans.length; i++) {
+ stops[i] = spans[i].getTabStop();
+ }
+ Arrays.sort(stops, 0, stops.length);
+ variableTabStops = stops;
+ }
+ }
+
+ int breakStrategy = 0; // 0 = kBreakStrategy_Greedy
+ nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
+ firstWidth, firstWidthLineCount, restWidth,
+ variableTabStops, TAB_INCREMENT, breakStrategy);
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
@@ -505,25 +525,9 @@ public class StaticLayout extends Layout {
spanEndCacheCount++;
}
- // tab stop locations
- int[] variableTabStops = null;
- if (spanned != null) {
- TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
- paraEnd, TabStopSpan.class);
- if (spans.length > 0) {
- int[] stops = new int[spans.length];
- for (int i = 0; i < spans.length; i++) {
- stops[i] = spans[i].getTabStop();
- }
- Arrays.sort(stops, 0, stops.length);
- variableTabStops = stops;
- }
- }
-
nGetWidths(b.mNativePtr, widths);
- int breakCount = nComputeLineBreaks(b.mNativePtr, paraEnd - paraStart, firstWidth,
- firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks,
- lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
+ int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
+ lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
int[] breaks = lineBreaks.breaks;
float[] lineWidths = lineBreaks.widths;
@@ -966,7 +970,10 @@ public class StaticLayout extends Layout {
private static native void nFinishBuilder(long nativePtr);
private static native void nSetLocale(long nativePtr, String locale);
- private static native void nSetText(long nativePtr, char[] text, int length);
+ // Set up paragraph text and settings; done as one big method to minimize jni crossings
+ private static native void nSetupParagraph(long nativePtr, char[] text, int length,
+ float firstWidth, int firstWidthLineCount, float restWidth,
+ int[] variableTabStops, int defaultTabStop, int breakStrategy);
private static native float nAddStyleRun(long nativePtr, long nativePaint,
long nativeTypeface, int start, int end, boolean isRtl);
@@ -983,9 +990,7 @@ public class StaticLayout extends Layout {
// the arrays inside the LineBreaks objects are passed in as well
// to reduce the number of JNI calls in the common case where the
// arrays do not have to be resized
- private static native int nComputeLineBreaks(long nativePtr,
- int length, float firstWidth, int firstWidthLineCount, float restWidth,
- int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
+ private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength);
private int mLineCount;
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index d29bfb6..0669b6f 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -23,6 +24,7 @@ import android.os.Parcel;
import android.provider.Browser;
import android.text.ParcelableSpan;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
public class URLSpan extends ClickableSpan implements ParcelableSpan {
@@ -59,6 +61,10 @@ public class URLSpan extends ClickableSpan implements ParcelableSpan {
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
- context.startActivity(intent);
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
+ }
}
}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 8e9eb48..c119277 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -525,10 +525,6 @@ public class Linkify {
return 0;
}
-
- public final boolean equals(Object o) {
- return false;
- }
};
Collections.sort(links, c);
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index e8d3947..9326203 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -18,6 +18,7 @@ package android.util;
import com.android.internal.util.ArrayUtils;
+import java.util.Arrays;
import libcore.util.EmptyArray;
/**
@@ -78,6 +79,24 @@ public class IntArray implements Cloneable {
}
/**
+ * Searches the array for the specified value using the binary search algorithm. The array must
+ * be sorted (as by the {@link Arrays#sort(int[], int, int)} method) prior to making this call.
+ * If it is not sorted, the results are undefined. If the range contains multiple elements with
+ * the specified value, there is no guarantee which one will be found.
+ *
+ * @param value The value to search for.
+ * @return index of the search key, if it is contained in the array; otherwise, <i>(-(insertion
+ * point) - 1)</i>. The insertion point is defined as the point at which the key would
+ * be inserted into the array: the index of the first element greater than the key, or
+ * {@link #size()} if all elements in the array are less than the specified key.
+ * Note that this guarantees that the return value will be >= 0 if and only if the key
+ * is found.
+ */
+ public int binarySearch(int value) {
+ return ContainerHelpers.binarySearch(mValues, mSize, value);
+ }
+
+ /**
* Adds the values in the specified array to this array.
*/
public void addAll(IntArray values) {
@@ -159,4 +178,11 @@ public class IntArray implements Cloneable {
public int size() {
return mSize;
}
+
+ /**
+ * Returns a new array with the contents of this IntArray.
+ */
+ public int[] toArray() {
+ return Arrays.copyOf(mValues, mSize);
+ }
}
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 92b19be..18dc262 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -164,7 +164,7 @@ public class PathParser {
* @return array of floats
*/
private static float[] getFloats(String s) {
- if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
+ if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
return new float[0];
}
try {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 90e1f86..3caf6f0 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -18,6 +18,7 @@ package android.view;
import android.annotation.NonNull;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.CanvasProperty;
import android.graphics.NinePatch;
import android.graphics.Paint;
@@ -32,8 +33,10 @@ import android.util.Pools.SynchronizedPool;
* This is intended for use with a DisplayList. This class keeps a list of all the Paint and
* Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
* the DisplayList is still holding a native reference to the memory.
+ *
+ * @hide
*/
-class DisplayListCanvas extends HardwareCanvas {
+public class DisplayListCanvas extends Canvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
private static final int POOL_LIMIT = 25;
@@ -85,7 +88,6 @@ class DisplayListCanvas extends HardwareCanvas {
// Constructors
///////////////////////////////////////////////////////////////////////////
-
private DisplayListCanvas() {
super(nCreateDisplayListRenderer());
}
@@ -103,6 +105,16 @@ class DisplayListCanvas extends HardwareCanvas {
///////////////////////////////////////////////////////////////////////////
@Override
+ public boolean isHardwareAccelerated() {
+ return true;
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isOpaque() {
return false;
}
@@ -171,7 +183,11 @@ class DisplayListCanvas extends HardwareCanvas {
private static native void nInsertReorderBarrier(long renderer, boolean enableReorder);
- @Override
+ /**
+ * Invoked before any drawing operation is performed in this canvas.
+ *
+ * @param dirty The dirty rectangle to update, can be null.
+ */
public void onPreDraw(Rect dirty) {
if (dirty != null) {
nPrepareDirty(mNativeCanvasWrapper, dirty.left, dirty.top, dirty.right, dirty.bottom);
@@ -183,7 +199,9 @@ class DisplayListCanvas extends HardwareCanvas {
private static native void nPrepare(long renderer);
private static native void nPrepareDirty(long renderer, int left, int top, int right, int bottom);
- @Override
+ /**
+ * Invoked after all drawing operation have been performed.
+ */
public void onPostDraw() {
nFinish(mNativeCanvasWrapper);
}
@@ -194,7 +212,13 @@ class DisplayListCanvas extends HardwareCanvas {
// Functor
///////////////////////////////////////////////////////////////////////////
- @Override
+ /**
+ * Calls the function specified with the drawGLFunction function pointer. This is
+ * functionality used by webkit for calling into their renderer from our display lists.
+ * This function may return true if an invalidation is needed after the call.
+ *
+ * @param drawGLFunction A native function pointer
+ */
public void callDrawGLFunction2(long drawGLFunction) {
nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction);
}
@@ -207,7 +231,23 @@ class DisplayListCanvas extends HardwareCanvas {
protected static native long nFinishRecording(long renderer);
- @Override
+ /**
+ * Draws the specified display list onto this canvas. The display list can only
+ * be drawn if {@link android.view.RenderNode#isValid()} returns true.
+ *
+ * @param renderNode The RenderNode to replay.
+ */
+ public void drawRenderNode(RenderNode renderNode) {
+ drawRenderNode(renderNode, RenderNode.FLAG_CLIP_CHILDREN);
+ }
+
+ /**
+ * Draws the specified display list onto this canvas.
+ *
+ * @param renderNode The RenderNode to replay.
+ * @param flags Optional flags about drawing, see {@link RenderNode} for
+ * the possible flags.
+ */
public void drawRenderNode(RenderNode renderNode, int flags) {
nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList(), flags);
}
@@ -219,6 +259,14 @@ class DisplayListCanvas extends HardwareCanvas {
// Hardware layer
///////////////////////////////////////////////////////////////////////////
+ /**
+ * Draws the specified layer onto this canvas.
+ *
+ * @param layer The layer to composite on this canvas
+ * @param x The left coordinate of the layer
+ * @param y The top coordinate of the layer
+ * @param paint The paint used to draw the layer
+ */
void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
layer.setLayerPaint(paint);
nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle(), x, y);
@@ -253,7 +301,6 @@ class DisplayListCanvas extends HardwareCanvas {
private static native void nDrawPatch(long renderer, long bitmap, long chunk,
float left, float top, float right, float bottom, long paint);
- @Override
public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
@@ -263,7 +310,6 @@ class DisplayListCanvas extends HardwareCanvas {
private static native void nDrawCircle(long renderer, long propCx,
long propCy, long propRadius, long propPaint);
- @Override
public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
CanvasProperty<Float> ry, CanvasProperty<Paint> paint) {
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index 20baad0..d58e7c0 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -46,14 +46,14 @@ public class GhostView extends View {
@Override
protected void onDraw(Canvas canvas) {
- if (canvas instanceof HardwareCanvas) {
- HardwareCanvas hwCanvas = (HardwareCanvas) canvas;
+ if (canvas instanceof DisplayListCanvas) {
+ DisplayListCanvas dlCanvas = (DisplayListCanvas) canvas;
mView.mRecreateDisplayList = true;
RenderNode renderNode = mView.getDisplayList();
if (renderNode.isValid()) {
- hwCanvas.insertReorderBarrier(); // enable shadow for this rendernode
- hwCanvas.drawRenderNode(renderNode);
- hwCanvas.insertInorderBarrier(); // re-disable reordering/shadows
+ dlCanvas.insertReorderBarrier(); // enable shadow for this rendernode
+ dlCanvas.drawRenderNode(renderNode);
+ dlCanvas.insertInorderBarrier(); // re-disable reordering/shadows
}
}
}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
deleted file mode 100644
index fc2b55b..0000000
--- a/core/java/android/view/HardwareCanvas.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2010 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.view;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.CanvasProperty;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-/**
- * Hardware accelerated canvas.
- *
- * @hide
- */
-public abstract class HardwareCanvas extends Canvas {
-
- /**
- * Pass a reference to the native renderer to our superclass's
- * constructor.
- */
- protected HardwareCanvas(long renderer) {
- super(renderer);
- }
-
- @Override
- public boolean isHardwareAccelerated() {
- return true;
- }
-
- @Override
- public void setBitmap(Bitmap bitmap) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Invoked before any drawing operation is performed in this canvas.
- *
- * @param dirty The dirty rectangle to update, can be null.
- *
- * @hide
- */
- public abstract void onPreDraw(Rect dirty);
-
- /**
- * Invoked after all drawing operation have been performed.
- *
- * @hide
- */
- public abstract void onPostDraw();
-
- /**
- * Draws the specified display list onto this canvas. The display list can only
- * be drawn if {@link android.view.RenderNode#isValid()} returns true.
- *
- * @param renderNode The RenderNode to replay.
- */
- public void drawRenderNode(RenderNode renderNode) {
- drawRenderNode(renderNode, RenderNode.FLAG_CLIP_CHILDREN);
- }
-
- /**
- * Draws the specified display list onto this canvas.
- *
- * @param renderNode The RenderNode to replay.
- * @param dirty Ignored, can be null.
- * @param flags Optional flags about drawing, see {@link RenderNode} for
- * the possible flags.
- *
- * @hide
- */
- public abstract void drawRenderNode(RenderNode renderNode, int flags);
-
- /**
- * Draws the specified layer onto this canvas.
- *
- * @param layer The layer to composite on this canvas
- * @param x The left coordinate of the layer
- * @param y The top coordinate of the layer
- * @param paint The paint used to draw the layer
- *
- * @hide
- */
- abstract void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint);
-
- /**
- * Calls the function specified with the drawGLFunction function pointer. This is
- * functionality used by webkit for calling into their renderer from our display lists.
- * This function may return true if an invalidation is needed after the call.
- *
- * @param drawGLFunction A native function pointer
- *
- * @hide
- */
- public void callDrawGLFunction2(long drawGLFunction) {
- // Noop - this is done in the display list recorder subclass
- }
-
- public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
- CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
-
- public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
- CanvasProperty<Float> right, CanvasProperty<Float> bottom,
- CanvasProperty<Float> rx, CanvasProperty<Float> ry,
- CanvasProperty<Paint> paint);
-
- public static void setProperty(String name, String value) {
- DisplayListCanvas.setProperty(name, value);
- }
-}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index afa7f51..6632f39 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -326,7 +326,7 @@ public abstract class HardwareRenderer {
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePreDraw(HardwareCanvas canvas);
+ void onHardwarePreDraw(DisplayListCanvas canvas);
/**
* Invoked after a view is drawn by a hardware renderer.
@@ -334,7 +334,7 @@ public abstract class HardwareRenderer {
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePostDraw(HardwareCanvas canvas);
+ void onHardwarePostDraw(DisplayListCanvas canvas);
}
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8ac8bc5..d6625c8 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -16,6 +16,7 @@
package android.view;
+import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -220,10 +221,14 @@ interface IWindowManager
boolean isRotationFrozen();
/**
+ * Used only for assist -- request a screenshot of the current application.
+ */
+ boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver);
+
+ /**
* Create a screenshot of the applications currently displayed.
*/
- Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth,
- int maxHeight, boolean force565);
+ Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight);
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 358ae8a..2eac549 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -49,6 +49,7 @@ public final class InputDevice implements Parcelable {
private final String mName;
private final int mVendorId;
private final int mProductId;
+ private final String mUniqueId;
private final String mDescriptor;
private final InputDeviceIdentifier mIdentifier;
private final boolean mIsExternal;
@@ -356,14 +357,16 @@ public final class InputDevice implements Parcelable {
// Called by native code.
private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
- int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
- KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasButtonUnderPad) {
+ int productId, String uniqueId, String descriptor, boolean isExternal, int sources,
+ int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator,
+ boolean hasButtonUnderPad) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
mName = name;
mVendorId = vendorId;
mProductId = productId;
+ mUniqueId = uniqueId;
mDescriptor = descriptor;
mIsExternal = isExternal;
mSources = sources;
@@ -381,6 +384,7 @@ public final class InputDevice implements Parcelable {
mName = in.readString();
mVendorId = in.readInt();
mProductId = in.readInt();
+ mUniqueId = in.readString();
mDescriptor = in.readString();
mIsExternal = in.readInt() != 0;
mSources = in.readInt();
@@ -505,6 +509,23 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the vendor's unique id for the given device, if available.
+ * <p>
+ * A vendor may assign a unique id to a device (e.g., MAC address for
+ * Bluetooth devices). A null value will be assigned where a unique id is
+ * not available.
+ * </p><p>
+ * This method is dependent on the vendor, whereas {@link #getDescriptor}
+ * attempts to create a unique id even when the vendor has not provided one.
+ * </p>
+ *
+ * @return The unique id of a given device
+ */
+ public String getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
* Gets the input device descriptor, which is a stable identifier for an input device.
* <p>
* An input device descriptor uniquely identifies an input device. Its value
@@ -843,6 +864,7 @@ public final class InputDevice implements Parcelable {
out.writeString(mName);
out.writeInt(mVendorId);
out.writeInt(mProductId);
+ out.writeString(mUniqueId);
out.writeString(mDescriptor);
out.writeInt(mIsExternal ? 1 : 0);
out.writeInt(mSources);
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 1a07aee..457d6ad 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -452,9 +452,10 @@ public abstract class LayoutInflater {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
+ final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
- Context lastContext = (Context)mConstructorArgs[0];
- mConstructorArgs[0] = mContext;
+ Context lastContext = (Context) mConstructorArgs[0];
+ mConstructorArgs[0] = inflaterContext;
View result = root;
try {
@@ -485,10 +486,10 @@ public abstract class LayoutInflater {
+ "ViewGroup root and attachToRoot=true");
}
- rInflate(parser, root, attrs, false, false);
+ rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
- final View temp = createViewFromTag(root, name, attrs, false);
+ final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
@@ -509,8 +510,10 @@ public abstract class LayoutInflater {
if (DEBUG) {
System.out.println("-----> start inflating children");
}
- // Inflate all children under temp
- rInflate(parser, temp, attrs, true, true);
+
+ // Inflate all children under temp against its context.
+ rInflateChildren(parser, temp, attrs, true);
+
if (DEBUG) {
System.out.println("-----> done inflating children");
}
@@ -692,59 +695,68 @@ public abstract class LayoutInflater {
}
/**
+ * Convenience method for calling through to the five-arg createViewFromTag
+ * method. This method passes {@code false} for the {@code ignoreThemeAttr}
+ * argument and should be used for everything except {@code &gt;include>}
+ * tag parsing.
+ */
+ private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
+ return createViewFromTag(parent, name, context, attrs, false);
+ }
+
+ /**
* Creates a view from a tag name using the supplied attribute set.
* <p>
- * If {@code inheritContext} is true and the parent is non-null, the view
- * will be inflated in parent view's context. If the view specifies a
- * &lt;theme&gt; attribute, the inflation context will be wrapped with the
- * specified theme.
- * <p>
- * Note: Default visibility so the BridgeInflater can override it.
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * override it.
+ *
+ * @param parent the parent view, used to inflate layout params
+ * @param name the name of the XML tag used to define the view
+ * @param context the inflation context for the view, typically the
+ * {@code parent} or base layout inflater context
+ * @param attrs the attribute set for the XML tag used to define the view
+ * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
+ * attribute (if set) for the view being inflated,
+ * {@code false} otherwise
*/
- View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) {
+ View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
+ boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
- Context viewContext;
- if (parent != null && inheritContext) {
- viewContext = parent.getContext();
- } else {
- viewContext = mContext;
- }
-
- // Apply a theme wrapper, if requested.
- final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);
- final int themeResId = ta.getResourceId(0, 0);
- if (themeResId != 0) {
- viewContext = new ContextThemeWrapper(viewContext, themeResId);
+ // Apply a theme wrapper, if allowed and one is specified.
+ if (!ignoreThemeAttr) {
+ final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+ final int themeResId = ta.getResourceId(0, 0);
+ if (themeResId != 0) {
+ context = new ContextThemeWrapper(context, themeResId);
+ }
+ ta.recycle();
}
- ta.recycle();
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
- return new BlinkLayout(viewContext, attrs);
+ return new BlinkLayout(context, attrs);
}
- if (DEBUG) System.out.println("******** Creating view: " + name);
-
try {
View view;
if (mFactory2 != null) {
- view = mFactory2.onCreateView(parent, name, viewContext, attrs);
+ view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
- view = mFactory.onCreateView(name, viewContext, attrs);
+ view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
- view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);
+ view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = viewContext;
+ mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
@@ -756,20 +768,18 @@ public abstract class LayoutInflater {
}
}
- if (DEBUG) System.out.println("Created view is: " + view);
return view;
-
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
} catch (Exception e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
@@ -777,16 +787,26 @@ public abstract class LayoutInflater {
}
/**
+ * Recursive method used to inflate internal (non-root) children. This
+ * method calls through to {@link #rInflate} using the parent context as
+ * the inflation context.
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * call it.
+ */
+ final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
+ boolean finishInflate) throws XmlPullParserException, IOException {
+ rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
+ }
+
+ /**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
- *
- * @param inheritContext Whether the root view should be inflated in its
- * parent's context. This should be true when called inflating
- * child views recursively, or false otherwise.
+ * <p>
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * override it.
*/
- void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
- boolean finishInflate, boolean inheritContext) throws XmlPullParserException,
- IOException {
+ void rInflate(XmlPullParser parser, View parent, Context context,
+ AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
@@ -808,19 +828,21 @@ public abstract class LayoutInflater {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
- parseInclude(parser, parent, attrs, inheritContext);
+ parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
- final View view = createViewFromTag(parent, name, attrs, inheritContext);
+ final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- rInflate(parser, view, attrs, true, true);
+ rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
- if (finishInflate) parent.onFinishInflate();
+ if (finishInflate) {
+ parent.onFinishInflate();
+ }
}
/**
@@ -829,13 +851,9 @@ public abstract class LayoutInflater {
*/
private void parseRequestFocus(XmlPullParser parser, View view)
throws XmlPullParserException, IOException {
- int type;
view.requestFocus();
- final int currentDepth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
+
+ consumeChildElements(parser);
}
/**
@@ -844,33 +862,29 @@ public abstract class LayoutInflater {
*/
private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
throws XmlPullParserException, IOException {
- int type;
-
- final TypedArray ta = view.getContext().obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.ViewTag);
- final int key = ta.getResourceId(com.android.internal.R.styleable.ViewTag_id, 0);
- final CharSequence value = ta.getText(com.android.internal.R.styleable.ViewTag_value);
+ final Context context = view.getContext();
+ final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
+ final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
+ final CharSequence value = ta.getText(R.styleable.ViewTag_value);
view.setTag(key, value);
ta.recycle();
- final int currentDepth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
+ consumeChildElements(parser);
}
- private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs,
- boolean inheritContext) throws XmlPullParserException, IOException {
+ private void parseInclude(XmlPullParser parser, Context context, View parent,
+ AttributeSet attrs) throws XmlPullParserException, IOException {
int type;
if (parent instanceof ViewGroup) {
- Context context = inheritContext ? parent.getContext() : mContext;
-
- // Apply a theme wrapper, if requested.
+ // Apply a theme wrapper, if requested. This is sort of a weird
+ // edge case, since developers think the <include> overwrites
+ // values in the AttributeSet of the included View. So, if the
+ // included View has a theme attribute, we'll need to ignore it.
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
- if (themeResId != 0) {
+ final boolean hasThemeOverride = themeResId != 0;
+ if (hasThemeOverride) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
@@ -880,11 +894,12 @@ public abstract class LayoutInflater {
int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
if (layout == 0) {
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
- if (value == null || value.length() < 1) {
+ if (value == null || value.length() <= 0) {
throw new InflateException("You must specify a layout in the"
+ " include tag: <include layout=\"@layout/layoutID\" />");
}
+ // Attempt to resolve the "?attr/name" string to an identifier.
layout = context.getResources().getIdentifier(value.substring(1), null, null);
}
@@ -901,8 +916,7 @@ public abstract class LayoutInflater {
throw new InflateException("You must specify a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
} else {
- final XmlResourceParser childParser =
- getContext().getResources().getLayout(layout);
+ final XmlResourceParser childParser = context.getResources().getLayout(layout);
try {
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
@@ -920,11 +934,12 @@ public abstract class LayoutInflater {
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) {
- // Inflate all children.
- rInflate(childParser, parent, childAttrs, false, inheritContext);
+ // The <merge> tag doesn't support android:theme, so
+ // nothing special to do here.
+ rInflate(childParser, parent, context, childAttrs, false);
} else {
- final View view = createViewFromTag(parent, childName, childAttrs,
- inheritContext);
+ final View view = createViewFromTag(parent, childName,
+ context, childAttrs, hasThemeOverride);
final ViewGroup group = (ViewGroup) parent;
final TypedArray a = context.obtainStyledAttributes(
@@ -957,7 +972,7 @@ public abstract class LayoutInflater {
view.setLayoutParams(params);
// Inflate all children.
- rInflate(childParser, view, childAttrs, true, true);
+ rInflateChildren(childParser, view, childAttrs, true);
if (id != View.NO_ID) {
view.setId(id);
@@ -985,6 +1000,16 @@ public abstract class LayoutInflater {
throw new InflateException("<include /> can only be used inside of a ViewGroup");
}
+ LayoutInflater.consumeChildElements(parser);
+ }
+
+ /**
+ * <strong>Note:</strong> default visibility so that
+ * LayoutInflater_Delegate can call it.
+ */
+ final static void consumeChildElements(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/android/view/PhoneFallbackEventHandler.java
index fbf5732..350650d 100644
--- a/core/java/android/view/PhoneFallbackEventHandler.java
+++ b/core/java/android/view/PhoneFallbackEventHandler.java
@@ -25,8 +25,13 @@ import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
import android.os.UserHandle;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
+import android.view.View;
+import android.view.HapticFeedbackConstants;
+import android.view.FallbackEventHandler;
+import android.view.KeyEvent;
/**
* @hide
@@ -112,15 +117,20 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
dispatcher.startTracking(event, this);
} else if (event.isLongPress() && dispatcher.isTracking(event)) {
dispatcher.performedLongPress(event);
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- // launch the VoiceDialer
- Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- sendCloseSystemWindows();
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- startCallActivity();
+ if (isUserSetupComplete()) {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ // launch the VoiceDialer
+ Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ sendCloseSystemWindows();
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ startCallActivity();
+ }
+ } else {
+ Log.i(TAG, "Not starting call activity because user "
+ + "setup is in progress.");
}
}
return true;
@@ -134,13 +144,18 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
dispatcher.startTracking(event, this);
} else if (event.isLongPress() && dispatcher.isTracking(event)) {
dispatcher.performedLongPress(event);
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
- // Broadcast an intent that the Camera button was longpressed
- Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
- null, null, null, 0, null, null);
+ if (isUserSetupComplete()) {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ sendCloseSystemWindows();
+ // Broadcast an intent that the Camera button was longpressed
+ Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
+ null, null, null, 0, null, null);
+ } else {
+ Log.i(TAG, "Not dispatching CAMERA long press because user "
+ + "setup is in progress.");
+ }
}
return true;
}
@@ -155,21 +170,26 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
Configuration config = mContext.getResources().getConfiguration();
if (config.keyboard == Configuration.KEYBOARD_NOKEYS
|| config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
- // launch the search activity
- Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
- getSearchManager().stopSearch();
- mContext.startActivity(intent);
- // Only clear this if we successfully start the
- // activity; otherwise we will allow the normal short
- // press action to be performed.
- dispatcher.performedLongPress(event);
- return true;
- } catch (ActivityNotFoundException e) {
- // Ignore
+ if (isUserSetupComplete()) {
+ // launch the search activity
+ Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ sendCloseSystemWindows();
+ getSearchManager().stopSearch();
+ mContext.startActivity(intent);
+ // Only clear this if we successfully start the
+ // activity; otherwise we will allow the normal short
+ // press action to be performed.
+ dispatcher.performedLongPress(event);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ // Ignore
+ }
+ } else {
+ Log.i(TAG, "Not dispatching SEARCH long press because user "
+ + "setup is in progress.");
}
}
}
@@ -181,7 +201,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
boolean onKeyUp(int keyCode, KeyEvent event) {
if (DEBUG) {
- Slog.d(TAG, "up " + keyCode);
+ Log.d(TAG, "up " + keyCode);
}
final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
if (dispatcher != null) {
@@ -229,7 +249,12 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
break;
}
if (event.isTracking() && !event.isCanceled()) {
- startCallActivity();
+ if (isUserSetupComplete()) {
+ startCallActivity();
+ } else {
+ Log.i(TAG, "Not starting call activity because user "
+ + "setup is in progress.");
+ }
}
return true;
}
@@ -244,7 +269,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
- Slog.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
+ Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
}
}
@@ -284,5 +309,10 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
private void handleMediaKeyEvent(KeyEvent keyEvent) {
MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ }
}
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 37859b8..cb32697 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -28,6 +28,7 @@ import android.app.SearchManager;
import android.os.UserHandle;
import com.android.internal.R;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
@@ -64,6 +65,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
@@ -125,7 +127,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
TypedValue mFixedWidthMinor;
TypedValue mFixedHeightMajor;
TypedValue mFixedHeightMinor;
- TypedValue mOutsetBottom;
+ int mOutsetBottomPx;
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@@ -1759,7 +1761,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
mVolumeControlStreamType, direction,
- AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
+ AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE
+ | AudioManager.FLAG_FROM_KEY);
}
return true;
}
@@ -1837,15 +1840,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN: {
+ final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
+ | AudioManager.FLAG_FROM_KEY;
// If we have a session send it the volume command, otherwise
// use the suggested stream.
if (mMediaController != null) {
- mMediaController.adjustVolume(0, AudioManager.FLAG_PLAY_SOUND
- | AudioManager.FLAG_VIBRATE);
+ mMediaController.adjustVolume(0, flags);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
- mVolumeControlStreamType, 0,
- AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE);
+ mVolumeControlStreamType, 0, flags);
}
return true;
}
@@ -2367,12 +2370,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- if (mOutsetBottom != null) {
- final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
- int bottom = (int) mOutsetBottom.getDimension(metrics);
+ if (mOutsetBottomPx != 0) {
WindowInsets newInsets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), bottom);
+ insets.getSystemWindowInsetRight(), mOutsetBottomPx);
return super.dispatchApplyWindowInsets(newInsets);
} else {
return super.dispatchApplyWindowInsets(insets);
@@ -2591,12 +2592,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
- if (mOutsetBottom != null) {
+ if (mOutsetBottomPx != 0) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
- int outset = (int) mOutsetBottom.getDimension(metrics);
int height = MeasureSpec.getSize(heightMeasureSpec);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + outset, mode);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + mOutsetBottomPx, mode);
}
}
@@ -2980,8 +2980,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (mStatusGuard == null) {
mStatusGuard = new View(mContext);
- mStatusGuard.setBackgroundColor(mContext.getResources()
- .getColor(R.color.input_method_navigation_guard));
+ mStatusGuard.setBackgroundColor(mContext.getColor(
+ R.color.input_method_navigation_guard));
addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
new LayoutParams(LayoutParams.MATCH_PARENT,
mlp.topMargin, Gravity.START | Gravity.TOP));
@@ -3040,8 +3040,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// position the navigation guard view, creating it if necessary
if (mNavigationGuard == null) {
mNavigationGuard = new View(mContext);
- mNavigationGuard.setBackgroundColor(mContext.getResources()
- .getColor(R.color.input_method_navigation_guard));
+ mNavigationGuard.setBackgroundColor(mContext.getColor(
+ R.color.input_method_navigation_guard));
addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
new LayoutParams(LayoutParams.MATCH_PARENT,
insets.getSystemWindowInsetBottom(),
@@ -3471,10 +3471,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
final boolean shouldUseBottomOutset =
display.getDisplayId() == Display.DEFAULT_DISPLAY
|| (getForcedWindowFlags() & FLAG_FULLSCREEN) != 0;
- if (shouldUseBottomOutset && a.hasValue(R.styleable.Window_windowOutsetBottom)) {
- if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
- a.getValue(R.styleable.Window_windowOutsetBottom,
- mOutsetBottom);
+ if (shouldUseBottomOutset) {
+ mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx(
+ getContext().getResources().getDisplayMetrics(), a);
}
}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 38867a8..ef98bbc 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -26,7 +26,7 @@ import android.graphics.Rect;
/**
* <p>A display list records a series of graphics related operations and can replay
* them later. Display lists are usually built by recording operations on a
- * {@link HardwareCanvas}. Replaying the operations from a display list avoids
+ * {@link DisplayListCanvas}. Replaying the operations from a display list avoids
* executing application code on every frame, and is thus much more efficient.</p>
*
* <p>Display lists are used internally for all views by default, and are not
@@ -43,7 +43,7 @@ import android.graphics.Rect;
* affected paragraph needs to be recorded again.</p>
*
* <h3>Hardware acceleration</h3>
- * <p>Display lists can only be replayed using a {@link HardwareCanvas}. They are not
+ * <p>Display lists can only be replayed using a {@link DisplayListCanvas}. They are not
* supported in software. Always make sure that the {@link android.graphics.Canvas}
* you are using to render a display list is hardware accelerated using
* {@link android.graphics.Canvas#isHardwareAccelerated()}.</p>
@@ -53,7 +53,7 @@ import android.graphics.Rect;
* HardwareRenderer renderer = myView.getHardwareRenderer();
* if (renderer != null) {
* DisplayList displayList = renderer.createDisplayList();
- * HardwareCanvas canvas = displayList.start(width, height);
+ * DisplayListCanvas canvas = displayList.start(width, height);
* try {
* // Draw onto the canvas
* // For instance: canvas.drawBitmap(...);
@@ -67,8 +67,8 @@ import android.graphics.Rect;
* <pre class="prettyprint">
* protected void onDraw(Canvas canvas) {
* if (canvas.isHardwareAccelerated()) {
- * HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas;
- * hardwareCanvas.drawDisplayList(mDisplayList);
+ * DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ * displayListCanvas.drawDisplayList(mDisplayList);
* }
* }
* </pre>
@@ -92,7 +92,7 @@ import android.graphics.Rect;
* <pre class="prettyprint">
* private void createDisplayList() {
* mDisplayList = DisplayList.create("MyDisplayList");
- * HardwareCanvas canvas = mDisplayList.start(width, height);
+ * DisplayListCanvas canvas = mDisplayList.start(width, height);
* try {
* for (Bitmap b : mBitmaps) {
* canvas.drawBitmap(b, 0.0f, 0.0f, null);
@@ -105,8 +105,8 @@ import android.graphics.Rect;
*
* protected void onDraw(Canvas canvas) {
* if (canvas.isHardwareAccelerated()) {
- * HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas;
- * hardwareCanvas.drawDisplayList(mDisplayList);
+ * DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ * displayListCanvas.drawDisplayList(mDisplayList);
* }
* }
*
@@ -128,7 +128,7 @@ import android.graphics.Rect;
public class RenderNode {
/**
* Flag used when calling
- * {@link HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)}
+ * {@link DisplayListCanvas#drawRenderNode
* When this flag is set, draw operations lying outside of the bounds of the
* display list will be culled early. It is recommeneded to always set this
* flag.
@@ -140,29 +140,29 @@ public class RenderNode {
/**
* Indicates that the display list is done drawing.
*
- * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)
+ * @see DisplayListCanvas#drawRenderNode(RenderNode, int)
*/
public static final int STATUS_DONE = 0x0;
/**
* Indicates that the display list needs another drawing pass.
*
- * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)
+ * @see DisplayListCanvas#drawRenderNode(RenderNode, int)
*/
public static final int STATUS_DRAW = 0x1;
/**
* Indicates that the display list needs to re-execute its GL functors.
*
- * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)
- * @see HardwareCanvas#callDrawGLFunction(long)
+ * @see DisplayListCanvas#drawRenderNode(RenderNode, int)
+ * @see DisplayListCanvas#callDrawGLFunction2(long)
*/
public static final int STATUS_INVOKE = 0x2;
/**
* Indicates that the display list performed GL drawing operations.
*
- * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)
+ * @see DisplayListCanvas#drawRenderNode(RenderNode, int)
*/
public static final int STATUS_DREW = 0x4;
@@ -213,7 +213,7 @@ public class RenderNode {
* stored in this display list.
*
* Calling this method will mark the render node invalid until
- * {@link #end(HardwareCanvas)} is called.
+ * {@link #end(DisplayListCanvas)} is called.
* Only valid render nodes can be replayed.
*
* @param width The width of the recording viewport
@@ -221,11 +221,11 @@ public class RenderNode {
*
* @return A canvas to record drawing operations.
*
- * @see #end(HardwareCanvas)
+ * @see #end(DisplayListCanvas)
* @see #isValid()
*/
- public HardwareCanvas start(int width, int height) {
- HardwareCanvas canvas = DisplayListCanvas.obtain(this);
+ public DisplayListCanvas start(int width, int height) {
+ DisplayListCanvas canvas = DisplayListCanvas.obtain(this);
canvas.setViewport(width, height);
// The dirty rect should always be null for a display list
canvas.onPreDraw(null);
@@ -240,7 +240,7 @@ public class RenderNode {
* @see #start(int, int)
* @see #isValid()
*/
- public void end(HardwareCanvas endCanvas) {
+ public void end(DisplayListCanvas endCanvas) {
if (!(endCanvas instanceof DisplayListCanvas)) {
throw new IllegalArgumentException("Passed an invalid canvas to end!");
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 83b8100..6de4d3e 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -572,7 +572,7 @@ public class Surface implements Parcelable {
private final class HwuiContext {
private final RenderNode mRenderNode;
private long mHwuiRenderer;
- private HardwareCanvas mCanvas;
+ private DisplayListCanvas mCanvas;
HwuiContext() {
mRenderNode = RenderNode.create("HwuiCanvas", null);
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 59ec058..ad34f02 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -723,6 +723,12 @@ public class TextureView extends View {
mSurface.release();
}
mSurface = surfaceTexture;
+
+ // If the view is visible, update the listener in the new surface to use
+ // the existing listener in the view.
+ if (((mViewFlags & VISIBILITY_MASK) == VISIBLE)) {
+ mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+ }
mUpdateSurface = true;
invalidateParentIfNeeded();
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 51fefe9..031be07 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -276,7 +276,7 @@ public class ThreadedRenderer extends HardwareRenderer {
updateViewTreeDisplayList(view);
if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
- HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
+ DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
try {
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d345bed..cfcc6fe 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -83,6 +83,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -3168,6 +3169,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private Drawable mBackground;
private TintInfo mBackgroundTint;
+ @ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
+ private ForegroundInfo mForegroundInfo;
+
/**
* RenderNode used for backgrounds.
* <p>
@@ -3182,13 +3186,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private String mTransitionName;
- private static class TintInfo {
+ static class TintInfo {
ColorStateList mTintList;
PorterDuff.Mode mTintMode;
boolean mHasTintMode;
boolean mHasTintList;
}
+ private static class ForegroundInfo {
+ private Drawable mDrawable;
+ private TintInfo mTintInfo;
+ private int mGravity = Gravity.FILL;
+ private boolean mInsidePadding = true;
+ private boolean mBoundsChanged = true;
+ private final Rect mSelfBounds = new Rect();
+ private final Rect mOverlayBounds = new Rect();
+ }
+
static class ListenerInfo {
/**
* Listener used to dispatch focus change events.
@@ -4056,6 +4070,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider,
PROVIDER_BACKGROUND));
break;
+ case R.styleable.View_foreground:
+ setForeground(a.getDrawable(attr));
+ break;
+ case R.styleable.View_foregroundGravity:
+ setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
+ break;
+ case R.styleable.View_foregroundTintMode:
+ setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null));
+ break;
+ case R.styleable.View_foregroundTint:
+ setForegroundTintList(a.getColorStateList(attr));
+ break;
+ case R.styleable.View_foregroundInsidePadding:
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+ mForegroundInfo.mInsidePadding = a.getBoolean(attr,
+ mForegroundInfo.mInsidePadding);
+ break;
}
}
@@ -4813,10 +4846,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
- if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
- if (showContextMenu(event.getX(), event.getY(), event.getMetaState())) {
- return true;
- }
+ if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
+ (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
+ showContextMenu(event.getX(), event.getY(), event.getMetaState());
+ mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
+ return true;
}
return false;
}
@@ -5781,6 +5815,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
+
+ info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
}
private View findLabelForView(View view, int labeledId) {
@@ -8228,6 +8264,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return true;
}
} break;
+ case R.id.accessibility_action_show_on_screen: {
+ if (mAttachInfo != null) {
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ getDrawingRect(r);
+ return requestRectangleOnScreen(r, true);
+ }
+ } break;
}
return false;
}
@@ -8801,6 +8844,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (dr != null && visible != dr.isVisible()) {
dr.setVisible(visible, false);
}
+ final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (fg != null && visible != fg.isVisible()) {
+ fg.setVisible(visible, false);
+ }
}
/**
@@ -9917,6 +9964,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
final AttachInfo ai = mAttachInfo;
if (ai != null) {
@@ -10755,6 +10805,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10820,6 +10873,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10879,6 +10935,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10935,6 +10994,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -14216,7 +14278,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int height = mBottom - mTop;
int layerType = getLayerType();
- final HardwareCanvas canvas = renderNode.start(width, height);
+ final DisplayListCanvas canvas = renderNode.start(width, height);
canvas.setHighContrastText(mAttachInfo.mHighContrastText);
try {
@@ -15197,7 +15259,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (layer != null && layer.isValid()) {
int restoreAlpha = mLayerPaint.getAlpha();
mLayerPaint.setAlpha((int) (alpha * 255));
- ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ ((DisplayListCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
mLayerPaint.setAlpha(restoreAlpha);
layerRendered = true;
} else {
@@ -15220,7 +15282,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
} else {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- ((HardwareCanvas) canvas).drawRenderNode(renderNode, flags);
+ ((DisplayListCanvas) canvas).drawRenderNode(renderNode, flags);
}
}
} else if (cache != null) {
@@ -15313,13 +15375,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Step 4, draw the children
dispatchDraw(canvas);
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
-
+ // Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
+ // Step 6, draw decorations (foreground, scrollbars)
+ onDrawForeground(canvas);
+
// we're done...
return;
}
@@ -15461,12 +15524,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
canvas.restoreToCount(saveCount);
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
-
+ // Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
+
+ // Step 6, draw decorations (foreground, scrollbars)
+ onDrawForeground(canvas);
}
/**
@@ -15494,7 +15558,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
- ((HardwareCanvas) canvas).drawRenderNode(renderNode);
+ ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
@@ -15531,7 +15595,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
- final HardwareCanvas canvas = renderNode.start(width, height);
+ final DisplayListCanvas canvas = renderNode.start(width, height);
// Reverse left/top translation done by drawable canvas, which will
// instead be applied by rendernode's LTRB bounds below. This way, the
@@ -15849,6 +15913,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
notifySubtreeAccessibilityStateChangedIfNeeded();
}
@@ -15992,6 +16059,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackground != null) {
mBackground.setLayoutDirection(layoutDirection);
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection);
+ }
mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
onResolveDrawables(layoutDirection);
}
@@ -16047,7 +16117,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@CallSuper
protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who);
+ return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who)
+ || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
}
/**
@@ -16065,9 +16136,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
protected void drawableStateChanged() {
final int[] state = getDrawableState();
- final Drawable d = mBackground;
- if (d != null && d.isStateful()) {
- d.setState(state);
+ final Drawable bg = mBackground;
+ if (bg != null && bg.isStateful()) {
+ bg.setState(state);
+ }
+
+ final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (fg != null && fg.isStateful()) {
+ fg.setState(state);
}
if (mScrollCache != null) {
@@ -16099,6 +16175,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackground != null) {
mBackground.setHotspot(x, y);
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setHotspot(x, y);
+ }
dispatchDrawableHotspotChanged(x, y);
}
@@ -16270,6 +16349,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mStateListAnimator != null) {
mStateListAnimator.jumpToCurrentState();
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.jumpToCurrentState();
+ }
}
/**
@@ -16554,6 +16636,249 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns the drawable used as the foreground of this View. The
+ * foreground drawable, if non-null, is always drawn on top of the view's content.
+ *
+ * @return a Drawable or null if no foreground was set
+ *
+ * @see #onDrawForeground(Canvas)
+ */
+ public Drawable getForeground() {
+ return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ }
+
+ /**
+ * Supply a Drawable that is to be rendered on top of all of the content in the view.
+ *
+ * @param foreground the Drawable to be drawn on top of the children
+ *
+ * @attr ref android.R.styleable#View_foreground
+ */
+ public void setForeground(Drawable foreground) {
+ if (mForegroundInfo == null) {
+ if (foreground == null) {
+ // Nothing to do.
+ return;
+ }
+ mForegroundInfo = new ForegroundInfo();
+ }
+
+ if (foreground == mForegroundInfo.mDrawable) {
+ // Nothing to do
+ return;
+ }
+
+ if (mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setCallback(null);
+ unscheduleDrawable(mForegroundInfo.mDrawable);
+ }
+
+ mForegroundInfo.mDrawable = foreground;
+ mForegroundInfo.mBoundsChanged = true;
+ if (foreground != null) {
+ setWillNotDraw(false);
+ foreground.setCallback(this);
+ foreground.setLayoutDirection(getLayoutDirection());
+ if (foreground.isStateful()) {
+ foreground.setState(getDrawableState());
+ }
+ applyForegroundTint();
+ }
+ requestLayout();
+ invalidate();
+ }
+
+ /**
+ * Magic bit used to support features of framework-internal window decor implementation details.
+ * This used to live exclusively in FrameLayout.
+ *
+ * @return true if the foreground should draw inside the padding region or false
+ * if it should draw inset by the view's padding
+ * @hide internal use only; only used by FrameLayout and internal screen layouts.
+ */
+ public boolean isForegroundInsidePadding() {
+ return mForegroundInfo != null ? mForegroundInfo.mInsidePadding : true;
+ }
+
+ /**
+ * Describes how the foreground is positioned.
+ *
+ * @return foreground gravity.
+ *
+ * @see #setForegroundGravity(int)
+ *
+ * @attr ref android.R.styleable#View_foregroundGravity
+ */
+ public int getForegroundGravity() {
+ return mForegroundInfo != null ? mForegroundInfo.mGravity
+ : Gravity.START | Gravity.TOP;
+ }
+
+ /**
+ * Describes how the foreground is positioned. Defaults to START and TOP.
+ *
+ * @param gravity see {@link android.view.Gravity}
+ *
+ * @see #getForegroundGravity()
+ *
+ * @attr ref android.R.styleable#View_foregroundGravity
+ */
+ public void setForegroundGravity(int gravity) {
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+
+ if (mForegroundInfo.mGravity != gravity) {
+ if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.START;
+ }
+
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.TOP;
+ }
+
+ mForegroundInfo.mGravity = gravity;
+ requestLayout();
+ }
+ }
+
+ /**
+ * Applies a tint to the foreground drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_foregroundTint
+ * @see #getForegroundTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setForegroundTintList(@Nullable ColorStateList tint) {
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+ if (mForegroundInfo.mTintInfo == null) {
+ mForegroundInfo.mTintInfo = new TintInfo();
+ }
+ mForegroundInfo.mTintInfo.mTintList = tint;
+ mForegroundInfo.mTintInfo.mHasTintList = true;
+
+ applyForegroundTint();
+ }
+
+ /**
+ * Return the tint applied to the foreground drawable, if specified.
+ *
+ * @return the tint applied to the foreground drawable
+ * @attr ref android.R.styleable#View_foregroundTint
+ * @see #setForegroundTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getForegroundTintList() {
+ return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+ ? mBackgroundTint.mTintList : null;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setForegroundTintList(ColorStateList)}} to the background
+ * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#View_foregroundTintMode
+ * @see #getForegroundTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ if (mBackgroundTint == null) {
+ mBackgroundTint = new TintInfo();
+ }
+ mBackgroundTint.mTintMode = tintMode;
+ mBackgroundTint.mHasTintMode = true;
+
+ applyBackgroundTint();
+ }
+
+ /**
+ * Return the blending mode used to apply the tint to the foreground
+ * drawable, if specified.
+ *
+ * @return the blending mode used to apply the tint to the foreground
+ * drawable
+ * @attr ref android.R.styleable#View_foregroundTintMode
+ * @see #setBackgroundTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getForegroundTintMode() {
+ return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+ ? mForegroundInfo.mTintInfo.mTintMode : null;
+ }
+
+ private void applyForegroundTint() {
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null
+ && mForegroundInfo.mTintInfo != null) {
+ final TintInfo tintInfo = mForegroundInfo.mTintInfo;
+ if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
+ mForegroundInfo.mDrawable = mForegroundInfo.mDrawable.mutate();
+
+ if (tintInfo.mHasTintList) {
+ mForegroundInfo.mDrawable.setTintList(tintInfo.mTintList);
+ }
+
+ if (tintInfo.mHasTintMode) {
+ mForegroundInfo.mDrawable.setTintMode(tintInfo.mTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mForegroundInfo.mDrawable.isStateful()) {
+ mForegroundInfo.mDrawable.setState(getDrawableState());
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw any foreground content for this view.
+ *
+ * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground}
+ * drawable or other view-specific decorations. The foreground is drawn on top of the
+ * primary view content.</p>
+ *
+ * @param canvas canvas to draw into
+ */
+ public void onDrawForeground(Canvas canvas) {
+ onDrawScrollBars(canvas);
+
+ final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (foreground != null) {
+ if (mForegroundInfo.mBoundsChanged) {
+ mForegroundInfo.mBoundsChanged = false;
+ final Rect selfBounds = mForegroundInfo.mSelfBounds;
+ final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
+
+ if (mForegroundInfo.mInsidePadding) {
+ selfBounds.set(0, 0, getWidth(), getHeight());
+ } else {
+ selfBounds.set(getPaddingLeft(), getPaddingTop(),
+ getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
+ }
+
+ final int ld = getLayoutDirection();
+ Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
+ foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
+ foreground.setBounds(overlayBounds);
+ }
+
+ foreground.draw(canvas);
+ }
+ }
+
+ /**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
@@ -18090,6 +18415,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// parts from this transparent region.
applyDrawableToTransparentRegion(mBackground, region);
}
+ final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (foreground != null) {
+ applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region);
+ }
}
return true;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1473806..294174a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -40,7 +40,6 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.media.AudioManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -77,6 +76,7 @@ import android.widget.Scroller;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -118,10 +118,11 @@ public final class ViewRootImpl implements ViewParent,
* at 60 Hz. This can be used to measure the potential framerate.
*/
private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
- private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
- // property used by emulator to determine display shape
+ // properties used by emulator to determine display shape
public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+ public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
+ "ro.emu.win_outset_bottom_px";
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -299,8 +300,6 @@ public final class ViewRootImpl implements ViewParent,
private Choreographer.FrameCallback mRenderProfiler;
private boolean mRenderProfilingEnabled;
- private boolean mMediaDisabled;
-
// Variables to track frames per second, enabled via DEBUG_FPS flag
private long mFpsStartTime = -1;
private long mFpsPrevTime = -1;
@@ -334,8 +333,6 @@ public final class ViewRootImpl implements ViewParent,
/** Set to true once doDie() has been called. */
private boolean mRemoved;
- private boolean mIsEmulator;
- private boolean mIsCircularEmulator;
private final boolean mWindowIsRound;
/**
@@ -392,8 +389,7 @@ public final class ViewRootImpl implements ViewParent,
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
- mWindowIsRound = context.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
+ mWindowIsRound = ScreenShapeHelper.getWindowIsRound(context.getResources());
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1248,9 +1244,8 @@ public final class ViewRootImpl implements ViewParent,
contentInsets = mPendingContentInsets;
stableInsets = mPendingStableInsets;
}
- final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound;
mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets, isRound);
+ null /* windowDecorInsets */, stableInsets, mWindowIsRound);
}
return mLastWindowInsets;
}
@@ -2309,12 +2304,12 @@ public final class ViewRootImpl implements ViewParent,
final Paint mResizePaint = new Paint();
@Override
- public void onHardwarePreDraw(HardwareCanvas canvas) {
+ public void onHardwarePreDraw(DisplayListCanvas canvas) {
canvas.translate(-mHardwareXOffset, -mHardwareYOffset);
}
@Override
- public void onHardwarePostDraw(HardwareCanvas canvas) {
+ public void onHardwarePostDraw(DisplayListCanvas canvas) {
if (mResizeBuffer != null) {
mResizePaint.setAlpha(mResizeAlpha);
canvas.drawHardwareLayer(mResizeBuffer, mHardwareXOffset, mHardwareYOffset,
@@ -5362,10 +5357,6 @@ public final class ViewRootImpl implements ViewParent,
public void playSoundEffect(int effectId) {
checkThread();
- if (mMediaDisabled) {
- return;
- }
-
try {
final AudioManager audioManager = getAudioManager();
@@ -5572,9 +5563,6 @@ public final class ViewRootImpl implements ViewParent,
mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
profileRendering(mAttachInfo.mHasWindowFocus);
- // Media (used by sound effects)
- mMediaDisabled = SystemProperties.getBoolean(PROPERTY_MEDIA_DISABLED, false);
-
// Hardware rendering
if (mAttachInfo.mHardwareRenderer != null) {
if (mAttachInfo.mHardwareRenderer.loadSystemProperties()) {
@@ -5590,11 +5578,6 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
}
}
-
- // detect emulator
- mIsEmulator = Build.HARDWARE.contains("goldfish");
- mIsCircularEmulator =
- SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false);
}
});
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 1cebe3f..57558ff 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -249,12 +249,12 @@ public final class WindowManagerGlobal {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
- } else if (ActivityManager.isHighEndGfx()) {
- // If there's no parent and we're running on L or above (or in the
- // system context), assume we want hardware acceleration.
+ } else {
+ // If there's no parent, then hardware acceleration for this view is
+ // set from the application's hardware acceleration setting.
final Context context = view.getContext();
- if (context != null && context.getApplicationInfo().targetSdkVersion
- >= Build.VERSION_CODES.LOLLIPOP) {
+ if (context != null
+ && context.getApplicationInfo().hardwareAccelerated) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6096d7d..77082b0 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -29,6 +29,8 @@ import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
import android.view.View;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -3402,6 +3404,15 @@ public class AccessibilityNodeInfo implements Parcelable {
new AccessibilityAction(
AccessibilityNodeInfo.ACTION_SET_TEXT, null);
+ /**
+ * Action that requests the node make its bounding rectangle visible
+ * on the screen, scrolling if necessary just enough.
+ *
+ * @see View#requestRectangleOnScreen(Rect)
+ */
+ public static final AccessibilityAction ACTION_SHOW_ON_SCREEN =
+ new AccessibilityAction(R.id.accessibility_action_show_on_screen, null);
+
private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
static {
sStandardActions.add(ACTION_FOCUS);
@@ -3426,6 +3437,7 @@ public class AccessibilityNodeInfo implements Parcelable {
sStandardActions.add(ACTION_COLLAPSE);
sStandardActions.add(ACTION_DISMISS);
sStandardActions.add(ACTION_SET_TEXT);
+ sStandardActions.add(ACTION_SHOW_ON_SCREEN);
}
private final int mActionId;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0996810..78604bf 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1992,8 +1992,8 @@ public final class InputMethodManager {
List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
// "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
ArrayList<InputMethodSubtype> subtypes = null;
- final int N = info.size();
- if (info != null && N > 0) {
+ if (info != null && !info.isEmpty()) {
+ final int N = info.size();
for (int i = 0; i < N; ++i) {
Object o = info.get(i);
if (o instanceof InputMethodInfo) {
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4737e9b..0b18bb8 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,13 +284,19 @@ public class WebChromeClient {
* currently set for that origin. The host application should invoke the
* specified callback with the desired permission state. See
* {@link GeolocationPermissions} for details.
+ *
+ * If this method isn't overridden, the callback is invoked with permission
+ * denied state.
+ *
* @param origin The origin of the web content attempting to use the
* Geolocation API.
* @param callback The callback to use to set the permission state for the
* origin.
*/
public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {}
+ GeolocationPermissions.Callback callback) {
+ callback.invoke(origin, false, false);
+ }
/**
* Notify the host application that a request for Geolocation permissions,
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6711a6b..aa77d5e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -299,7 +299,6 @@ public class WebView extends AbsoluteLayout
"android.webkit.DATA_REDUCTION_PROXY_SETTING_CHANGED";
private static final String LOGTAG = "WebView";
- private static final boolean TRACE = false;
// Throwing an exception for incorrect thread usage if the
// build target is JB MR2 or newer. Defaults to false, and is
@@ -599,7 +598,6 @@ public class WebView extends AbsoluteLayout
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
- if (TRACE) Log.d(LOGTAG, "WebView<init>");
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
@@ -614,7 +612,6 @@ public class WebView extends AbsoluteLayout
*/
public void setHorizontalScrollbarOverlay(boolean overlay) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setHorizontalScrollbarOverlay=" + overlay);
mProvider.setHorizontalScrollbarOverlay(overlay);
}
@@ -625,7 +622,6 @@ public class WebView extends AbsoluteLayout
*/
public void setVerticalScrollbarOverlay(boolean overlay) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setVerticalScrollbarOverlay=" + overlay);
mProvider.setVerticalScrollbarOverlay(overlay);
}
@@ -680,7 +676,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setCertificate(SslCertificate certificate) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setCertificate=" + certificate);
mProvider.setCertificate(certificate);
}
@@ -704,7 +699,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void savePassword(String host, String username, String password) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "savePassword=" + host);
mProvider.savePassword(host, username, password);
}
@@ -724,7 +718,6 @@ public class WebView extends AbsoluteLayout
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setHttpAuthUsernamePassword=" + host);
mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -754,7 +747,6 @@ public class WebView extends AbsoluteLayout
*/
public void destroy() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "destroy");
mProvider.destroy();
}
@@ -800,7 +792,6 @@ public class WebView extends AbsoluteLayout
*/
public void setNetworkAvailable(boolean networkUp) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setNetworkAvailable=" + networkUp);
mProvider.setNetworkAvailable(networkUp);
}
@@ -817,7 +808,6 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList saveState(Bundle outState) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "saveState");
return mProvider.saveState(outState);
}
@@ -834,7 +824,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "savePicture=" + dest.getName());
return mProvider.savePicture(b, dest);
}
@@ -852,7 +841,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean restorePicture(Bundle b, File src) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "restorePicture=" + src.getName());
return mProvider.restorePicture(b, src);
}
@@ -870,7 +858,6 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "restoreState");
return mProvider.restoreState(inState);
}
@@ -887,15 +874,6 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
- if (TRACE) {
- StringBuilder headers = new StringBuilder();
- if (additionalHttpHeaders != null) {
- for (Map.Entry<String, String> entry : additionalHttpHeaders.entrySet()) {
- headers.append(entry.getKey() + ":" + entry.getValue() + "\n");
- }
- }
- Log.d(LOGTAG, "loadUrl(extra headers)=" + url + "\n" + headers);
- }
mProvider.loadUrl(url, additionalHttpHeaders);
}
@@ -906,7 +884,6 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "loadUrl=" + url);
mProvider.loadUrl(url);
}
@@ -921,7 +898,6 @@ public class WebView extends AbsoluteLayout
*/
public void postUrl(String url, byte[] postData) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "postUrl=" + url);
if (URLUtil.isNetworkUrl(url)) {
mProvider.postUrl(url, postData);
} else {
@@ -960,7 +936,6 @@ public class WebView extends AbsoluteLayout
*/
public void loadData(String data, String mimeType, String encoding) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "loadData");
mProvider.loadData(data, mimeType, encoding);
}
@@ -993,7 +968,6 @@ public class WebView extends AbsoluteLayout
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@@ -1010,7 +984,6 @@ public class WebView extends AbsoluteLayout
*/
public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "evaluateJavascript=" + script);
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -1021,7 +994,6 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String filename) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "saveWebArchive=" + filename);
mProvider.saveWebArchive(filename);
}
@@ -1039,7 +1011,6 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "saveWebArchive(auto)=" + basename);
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -1048,7 +1019,6 @@ public class WebView extends AbsoluteLayout
*/
public void stopLoading() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "stopLoading");
mProvider.stopLoading();
}
@@ -1057,7 +1027,6 @@ public class WebView extends AbsoluteLayout
*/
public void reload() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "reload");
mProvider.reload();
}
@@ -1076,7 +1045,6 @@ public class WebView extends AbsoluteLayout
*/
public void goBack() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "goBack");
mProvider.goBack();
}
@@ -1095,7 +1063,6 @@ public class WebView extends AbsoluteLayout
*/
public void goForward() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "goForward");
mProvider.goForward();
}
@@ -1121,7 +1088,6 @@ public class WebView extends AbsoluteLayout
*/
public void goBackOrForward(int steps) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "goBackOrForwad=" + steps);
mProvider.goBackOrForward(steps);
}
@@ -1141,7 +1107,6 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageUp(boolean top) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "pageUp");
return mProvider.pageUp(top);
}
@@ -1153,7 +1118,6 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageDown(boolean bottom) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "pageDown");
return mProvider.pageDown(bottom);
}
@@ -1207,7 +1171,6 @@ public class WebView extends AbsoluteLayout
*/
public void insertVisualStateCallback(long requestId, VisualStateCallback callback) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "insertVisualStateCallback");
mProvider.insertVisualStateCallback(requestId, callback);
}
@@ -1220,7 +1183,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void clearView() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearView");
mProvider.clearView();
}
@@ -1251,7 +1213,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public Picture capturePicture() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "capturePicture");
return mProvider.capturePicture();
}
@@ -1262,7 +1223,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public PrintDocumentAdapter createPrintDocumentAdapter() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "createPrintDocumentAdapter");
return mProvider.createPrintDocumentAdapter("default");
}
@@ -1281,7 +1241,6 @@ public class WebView extends AbsoluteLayout
*/
public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "createPrintDocumentAdapter");
return mProvider.createPrintDocumentAdapter(documentName);
}
@@ -1321,7 +1280,6 @@ public class WebView extends AbsoluteLayout
*/
public void setInitialScale(int scaleInPercent) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setInitialScale=" + scaleInPercent);
mProvider.setInitialScale(scaleInPercent);
}
@@ -1332,7 +1290,6 @@ public class WebView extends AbsoluteLayout
*/
public void invokeZoomPicker() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "invokeZoomPicker");
mProvider.invokeZoomPicker();
}
@@ -1356,7 +1313,6 @@ public class WebView extends AbsoluteLayout
*/
public HitTestResult getHitTestResult() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "getHitTestResult");
return mProvider.getHitTestResult();
}
@@ -1375,7 +1331,6 @@ public class WebView extends AbsoluteLayout
*/
public void requestFocusNodeHref(Message hrefMsg) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "requestFocusNodeHref");
mProvider.requestFocusNodeHref(hrefMsg);
}
@@ -1388,7 +1343,6 @@ public class WebView extends AbsoluteLayout
*/
public void requestImageRef(Message msg) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "requestImageRef");
mProvider.requestImageRef(msg);
}
@@ -1493,7 +1447,6 @@ public class WebView extends AbsoluteLayout
*/
public void pauseTimers() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "pauseTimers");
mProvider.pauseTimers();
}
@@ -1503,7 +1456,6 @@ public class WebView extends AbsoluteLayout
*/
public void resumeTimers() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "resumeTimers");
mProvider.resumeTimers();
}
@@ -1516,7 +1468,6 @@ public class WebView extends AbsoluteLayout
*/
public void onPause() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "onPause");
mProvider.onPause();
}
@@ -1525,7 +1476,6 @@ public class WebView extends AbsoluteLayout
*/
public void onResume() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "onResume");
mProvider.onResume();
}
@@ -1548,7 +1498,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void freeMemory() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "freeMemory");
mProvider.freeMemory();
}
@@ -1560,7 +1509,6 @@ public class WebView extends AbsoluteLayout
*/
public void clearCache(boolean includeDiskFiles) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearCache");
mProvider.clearCache(includeDiskFiles);
}
@@ -1572,7 +1520,6 @@ public class WebView extends AbsoluteLayout
*/
public void clearFormData() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearFormData");
mProvider.clearFormData();
}
@@ -1581,7 +1528,6 @@ public class WebView extends AbsoluteLayout
*/
public void clearHistory() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearHistory");
mProvider.clearHistory();
}
@@ -1591,7 +1537,6 @@ public class WebView extends AbsoluteLayout
*/
public void clearSslPreferences() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearSslPreferences");
mProvider.clearSslPreferences();
}
@@ -1607,7 +1552,6 @@ public class WebView extends AbsoluteLayout
* callback. The runnable will be called in UI thread.
*/
public static void clearClientCertPreferences(Runnable onCleared) {
- if (TRACE) Log.d(LOGTAG, "clearClientCertPreferences");
getFactory().getStatics().clearClientCertPreferences(onCleared);
}
@@ -1649,7 +1593,6 @@ public class WebView extends AbsoluteLayout
*/
public void findNext(boolean forward) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "findNext");
mProvider.findNext(forward);
}
@@ -1665,7 +1608,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public int findAll(String find) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "findAll");
StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
return mProvider.findAll(find);
}
@@ -1680,7 +1622,6 @@ public class WebView extends AbsoluteLayout
*/
public void findAllAsync(String find) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "findAllAsync");
mProvider.findAllAsync(find);
}
@@ -1701,7 +1642,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean showFindDialog(String text, boolean showIme) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "showFindDialog");
return mProvider.showFindDialog(text, showIme);
}
@@ -1758,7 +1698,6 @@ public class WebView extends AbsoluteLayout
*/
public void clearMatches() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "clearMatches");
mProvider.clearMatches();
}
@@ -1819,7 +1758,6 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setPictureListener(PictureListener listener) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "setPictureListener=" + listener);
mProvider.setPictureListener(listener);
}
@@ -1876,7 +1814,6 @@ public class WebView extends AbsoluteLayout
*/
public void addJavascriptInterface(Object object, String name) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "addJavascriptInterface=" + name);
mProvider.addJavascriptInterface(object, name);
}
@@ -1889,7 +1826,6 @@ public class WebView extends AbsoluteLayout
*/
public void removeJavascriptInterface(String name) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "removeJavascriptInterface=" + name);
mProvider.removeJavascriptInterface(name);
}
@@ -1905,7 +1841,6 @@ public class WebView extends AbsoluteLayout
*/
public WebMessagePort[] createWebMessageChannel() {
checkThread();
- if (TRACE) Log.d(LOGTAG, "createWebMessageChannel");
return mProvider.createWebMessageChannel();
}
@@ -1920,7 +1855,6 @@ public class WebView extends AbsoluteLayout
*/
public void postMessageToMainFrame(WebMessage message, Uri targetOrigin) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "postMessageToMainFrame. TargetOrigin=" + targetOrigin);
mProvider.postMessageToMainFrame(message, targetOrigin);
}
@@ -2024,7 +1958,6 @@ public class WebView extends AbsoluteLayout
public void flingScroll(int vx, int vy) {
checkThread();
- if (TRACE) Log.d(LOGTAG, "flingScroll");
mProvider.flingScroll(vx, vy);
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index ac360fa..23af384 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -25,7 +25,7 @@ import android.graphics.Canvas;
import android.os.SystemProperties;
import android.os.Trace;
import android.util.SparseArray;
-import android.view.HardwareCanvas;
+import android.view.DisplayListCanvas;
import android.view.View;
import android.view.ViewRootImpl;
@@ -101,12 +101,12 @@ public final class WebViewDelegate {
* @throws IllegalArgumentException if the canvas is not hardware accelerated
*/
public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
- if (!(canvas instanceof HardwareCanvas)) {
+ if (!(canvas instanceof DisplayListCanvas)) {
// Canvas#isHardwareAccelerated() is only true for subclasses of HardwareCanvas.
throw new IllegalArgumentException(canvas.getClass().getName()
- + " is not hardware accelerated");
+ + " is not a DisplayList canvas");
}
- ((HardwareCanvas) canvas).callDrawGLFunction2(nativeDrawGLFunctor);
+ ((DisplayListCanvas) canvas).callDrawGLFunction2(nativeDrawGLFunctor);
}
/**
@@ -153,7 +153,7 @@ public final class WebViewDelegate {
}
/**
- * Adds the WebView asset path to {@link AssetManager}.
+ * Adds the WebView asset path to {@link android.content.res.AssetManager}.
*/
public void addWebViewAssetPath(Context context) {
context.getAssets().addAssetPath(
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 5c05b5a..6feb94b 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,6 +16,7 @@
*/
package android.widget;
+import android.os.UserHandle;
import com.android.internal.R;
import android.app.AlertDialog;
@@ -243,7 +244,8 @@ public class AppSecurityPermissions {
@Override
public void onClick(DialogInterface dialog, int which) {
PackageManager pm = getContext().getPackageManager();
- pm.revokePermission(mPackageName, mPerm.name);
+ pm.revokePermission(mPackageName, mPerm.name,
+ new UserHandle(mContext.getUserId()));
PermissionItemView.this.setVisibility(View.GONE);
}
};
@@ -298,7 +300,7 @@ public class AppSecurityPermissions {
}
extractPerms(info, permSet, installedPkgInfo);
}
- // Get permissions related to shared user if any
+ // Get permissions related to shared user if any
if (info.sharedUserId != null) {
int sharedUid;
try {
@@ -358,7 +360,7 @@ public class AppSecurityPermissions {
String permName = strList[i];
// If we are only looking at an existing app, then we only
// care about permissions that have actually been granted to it.
- if (installedPkgInfo != null && info == installedPkgInfo) {
+ if (installedPkgInfo != null && info != installedPkgInfo) {
if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
continue;
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 5bc16cb..2aaa356 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -18,6 +18,7 @@ package android.widget;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.StyleRes;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
@@ -31,6 +32,7 @@ import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -118,7 +120,9 @@ public class CalendarView extends FrameLayout {
* @param count The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setShownWeekCount(int count) {
mDelegate.setShownWeekCount(count);
}
@@ -129,7 +133,9 @@ public class CalendarView extends FrameLayout {
* @return The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public int getShownWeekCount() {
return mDelegate.getShownWeekCount();
}
@@ -140,7 +146,9 @@ public class CalendarView extends FrameLayout {
* @param color The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedWeekBackgroundColor(@ColorInt int color) {
mDelegate.setSelectedWeekBackgroundColor(color);
}
@@ -151,8 +159,10 @@ public class CalendarView extends FrameLayout {
* @return The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getSelectedWeekBackgroundColor() {
return mDelegate.getSelectedWeekBackgroundColor();
}
@@ -163,7 +173,9 @@ public class CalendarView extends FrameLayout {
* @param color The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setFocusedMonthDateColor(@ColorInt int color) {
mDelegate.setFocusedMonthDateColor(color);
}
@@ -174,8 +186,10 @@ public class CalendarView extends FrameLayout {
* @return The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getFocusedMonthDateColor() {
return mDelegate.getFocusedMonthDateColor();
}
@@ -186,7 +200,9 @@ public class CalendarView extends FrameLayout {
* @param color A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setUnfocusedMonthDateColor(@ColorInt int color) {
mDelegate.setUnfocusedMonthDateColor(color);
}
@@ -197,8 +213,10 @@ public class CalendarView extends FrameLayout {
* @return A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getUnfocusedMonthDateColor() {
return mDelegate.getUnfocusedMonthDateColor();
}
@@ -209,7 +227,9 @@ public class CalendarView extends FrameLayout {
* @param color The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekNumberColor(@ColorInt int color) {
mDelegate.setWeekNumberColor(color);
}
@@ -220,8 +240,10 @@ public class CalendarView extends FrameLayout {
* @return The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekNumberColor() {
return mDelegate.getWeekNumberColor();
}
@@ -232,7 +254,9 @@ public class CalendarView extends FrameLayout {
* @param color The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekSeparatorLineColor(@ColorInt int color) {
mDelegate.setWeekSeparatorLineColor(color);
}
@@ -243,8 +267,10 @@ public class CalendarView extends FrameLayout {
* @return The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekSeparatorLineColor() {
return mDelegate.getWeekSeparatorLineColor();
}
@@ -256,7 +282,9 @@ public class CalendarView extends FrameLayout {
* @param resourceId The vertical bar drawable resource id.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(@DrawableRes int resourceId) {
mDelegate.setSelectedDateVerticalBar(resourceId);
}
@@ -268,7 +296,9 @@ public class CalendarView extends FrameLayout {
* @param drawable The vertical bar drawable.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(Drawable drawable) {
mDelegate.setSelectedDateVerticalBar(drawable);
}
@@ -278,7 +308,9 @@ public class CalendarView extends FrameLayout {
* the end of the selected date.
*
* @return The vertical bar drawable.
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public Drawable getSelectedDateVerticalBar() {
return mDelegate.getSelectedDateVerticalBar();
}
@@ -519,29 +551,36 @@ public class CalendarView extends FrameLayout {
void setShownWeekCount(int count);
int getShownWeekCount();
- void setSelectedWeekBackgroundColor(int color);
+ void setSelectedWeekBackgroundColor(@ColorInt int color);
+ @ColorInt
int getSelectedWeekBackgroundColor();
- void setFocusedMonthDateColor(int color);
+ void setFocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getFocusedMonthDateColor();
- void setUnfocusedMonthDateColor(int color);
+ void setUnfocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getUnfocusedMonthDateColor();
- void setWeekNumberColor(int color);
+ void setWeekNumberColor(@ColorInt int color);
+ @ColorInt
int getWeekNumberColor();
- void setWeekSeparatorLineColor(int color);
+ void setWeekSeparatorLineColor(@ColorInt int color);
+ @ColorInt
int getWeekSeparatorLineColor();
- void setSelectedDateVerticalBar(int resourceId);
+ void setSelectedDateVerticalBar(@DrawableRes int resourceId);
void setSelectedDateVerticalBar(Drawable drawable);
Drawable getSelectedDateVerticalBar();
- void setWeekDayTextAppearance(int resourceId);
+ void setWeekDayTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getWeekDayTextAppearance();
- void setDateTextAppearance(int resourceId);
+ void setDateTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getDateTextAppearance();
void setMinDate(long minDate);
@@ -569,18 +608,12 @@ public class CalendarView extends FrameLayout {
* An abstract class which can be used as a start for CalendarView implementations
*/
abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
- /** String for parsing dates. */
- private static final String DATE_FORMAT = "MM/dd/yyyy";
-
/** The default minimal date. */
protected static final String DEFAULT_MIN_DATE = "01/01/1900";
/** The default maximal date. */
protected static final String DEFAULT_MAX_DATE = "01/01/2100";
- /** Date format for parsing dates. */
- protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
-
protected CalendarView mDelegator;
protected Context mContext;
protected Locale mCurrentLocale;
@@ -600,21 +633,131 @@ public class CalendarView extends FrameLayout {
mCurrentLocale = locale;
}
- /**
- * Parses the given <code>date</code> and in case of success sets
- * the result to the <code>outDate</code>.
- *
- * @return True if the date was parsed.
- */
- protected boolean parseDate(String date, Calendar outDate) {
- try {
- outDate.setTime(DATE_FORMATTER.parse(date));
- return true;
- } catch (ParseException e) {
- Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
- return false;
- }
+ @Override
+ public void setShownWeekCount(int count) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getFocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekNumberColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekNumberColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekSeparatorLineColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(@DrawableRes int resId) {
+ // Deprecated.
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ // Deprecated.
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ // Deprecated.
+ return null;
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ // Deprecated.
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ // Deprecated.
+ return false;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Nothing to do here, configuration changes are already propagated
+ // by ViewGroup.
}
}
+ /** String for parsing dates. */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+ /** Date format for parsing dates. */
+ private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
+ /**
+ * Utility method for the date format used by CalendarView's min/max date.
+ *
+ * @hide Use only as directed. For internal use only.
+ */
+ public static boolean parseDate(String date, Calendar outDate) {
+ if (date == null || date.isEmpty()) {
+ return false;
+ }
+
+ try {
+ final Date parsedDate = DATE_FORMATTER.parse(date);
+ outDate.setTime(parsedDate);
+ return true;
+ } catch (ParseException e) {
+ Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 2ab3548..6ab3828 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -27,7 +27,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -267,12 +266,12 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega
mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
LocaleData.get(Locale.getDefault()).firstDayOfWeek);
final String minDate = a.getString(R.styleable.CalendarView_minDate);
- if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
- parseDate(DEFAULT_MIN_DATE, mMinDate);
+ if (!CalendarView.parseDate(minDate, mMinDate)) {
+ CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
}
final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
- if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
- parseDate(DEFAULT_MAX_DATE, mMaxDate);
+ if (!CalendarView.parseDate(maxDate, mMaxDate)) {
+ CalendarView.parseDate(DEFAULT_MAX_DATE, mMaxDate);
}
if (mMaxDate.before(mMinDate)) {
throw new IllegalArgumentException("Max date cannot be before min date.");
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index b0f3740..7bce756 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -16,20 +16,11 @@
package android.widget;
-import com.android.internal.R;
-
+import android.annotation.StyleRes;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.MathUtils;
import java.util.Calendar;
-import java.util.Locale;
-
-import libcore.icu.LocaleData;
class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
private final DayPickerView mDayPickerView;
@@ -40,142 +31,32 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
int defStyleAttr, int defStyleRes) {
super(delegator, context);
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.CalendarView, defStyleAttr, defStyleRes);
- final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
-
- final long minDate = parseDateToMillis(a.getString(
- R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
- final long maxDate = parseDateToMillis(a.getString(
- R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
- if (maxDate < minDate) {
- throw new IllegalArgumentException("max date cannot be before min date");
- }
-
- final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
- final int dateTextAppearanceResId = a.getResourceId(
- R.styleable.CalendarView_dateTextAppearance,
- R.style.TextAppearance_DeviceDefault_Small);
-
- a.recycle();
-
- mDayPickerView = new DayPickerView(context);
- mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
- mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
- mDayPickerView.setMinDate(minDate);
- mDayPickerView.setMaxDate(maxDate);
- mDayPickerView.setDate(setDate, false, true);
+ mDayPickerView = new DayPickerView(context, attrs, defStyleAttr, defStyleRes);
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
delegator.addView(mDayPickerView);
}
- private long parseDateToMillis(String dateStr, String defaultDateStr) {
- final Calendar tempCalendar = Calendar.getInstance();
- if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
- parseDate(defaultDateStr, tempCalendar);
- }
- return tempCalendar.getTimeInMillis();
- }
-
@Override
- public void setShownWeekCount(int count) {
- // Deprecated.
- }
-
- @Override
- public int getShownWeekCount() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setSelectedWeekBackgroundColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getSelectedWeekBackgroundColor() {
- return 0;
- }
-
- @Override
- public void setFocusedMonthDateColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getFocusedMonthDateColor() {
- return 0;
- }
-
- @Override
- public void setUnfocusedMonthDateColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getUnfocusedMonthDateColor() {
- return 0;
- }
-
- @Override
- public void setWeekDayTextAppearance(int resourceId) {
-
+ public void setWeekDayTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayOfWeekTextAppearance(resId);
}
+ @StyleRes
@Override
public int getWeekDayTextAppearance() {
- return 0;
+ return mDayPickerView.getDayOfWeekTextAppearance();
}
@Override
- public void setDateTextAppearance(int resourceId) {
-
+ public void setDateTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayTextAppearance(resId);
}
+ @StyleRes
@Override
public int getDateTextAppearance() {
- return 0;
- }
-
- @Override
- public void setWeekNumberColor(int color) {
- // Deprecated.
- }
-
- @Override
- public int getWeekNumberColor() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setWeekSeparatorLineColor(int color) {
- // Deprecated.
- }
-
- @Override
- public int getWeekSeparatorLineColor() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setSelectedDateVerticalBar(int resourceId) {
- // Deprecated.
- }
-
- @Override
- public void setSelectedDateVerticalBar(Drawable drawable) {
- // Deprecated.
- }
-
- @Override
- public Drawable getSelectedDateVerticalBar() {
- // Deprecated.
- return null;
+ return mDayPickerView.getDayTextAppearance();
}
@Override
@@ -199,17 +80,6 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
}
@Override
- public void setShowWeekNumber(boolean showWeekNumber) {
- // Deprecated.
- }
-
- @Override
- public boolean getShowWeekNumber() {
- // Deprecated.
- return false;
- }
-
- @Override
public void setFirstDayOfWeek(int firstDayOfWeek) {
mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
}
@@ -221,12 +91,12 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
@Override
public void setDate(long date) {
- mDayPickerView.setDate(date, true, false);
+ mDayPickerView.setDate(date, true);
}
@Override
public void setDate(long date, boolean animate, boolean center) {
- mDayPickerView.setDate(date, animate, center);
+ mDayPickerView.setDate(date, animate);
}
@Override
@@ -239,12 +109,6 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
mOnDateChangeListener = listener;
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // Nothing to do here, configuration changes are already propagated
- // by ViewGroup.
- }
-
private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
new DayPickerView.OnDaySelectedListener() {
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 45998f7..5f5943f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -98,7 +98,7 @@ public class DatePicker extends FrameLayout {
private final DatePickerDelegate mDelegate;
/**
- * The callback used to indicate the user changes\d the date.
+ * The callback used to indicate the user changed the date.
*/
public interface OnDateChangedListener {
@@ -348,9 +348,13 @@ public class DatePicker extends FrameLayout {
}
/**
- * Gets whether the {@link CalendarView} is shown.
+ * Returns whether the {@link CalendarView} is shown.
+ * <p>
+ * <strong>Note:</strong> This method returns {@code false} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @return True if the calendar view is shown.
+ * @return {@code true} if the calendar view is shown
* @see #getCalendarView()
*/
public boolean getCalendarViewShown() {
@@ -358,13 +362,13 @@ public class DatePicker extends FrameLayout {
}
/**
- * Gets the {@link CalendarView}.
+ * Returns the {@link CalendarView} used by this picker.
* <p>
- * This method returns {@code null} when the
+ * <strong>Note:</strong> This method returns {@code null} when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
- * @return The calendar view.
+ * @return the calendar view
* @see #getCalendarViewShown()
*/
public CalendarView getCalendarView() {
@@ -374,20 +378,25 @@ public class DatePicker extends FrameLayout {
/**
* Sets whether the {@link CalendarView} is shown.
* <p>
- * Calling this method has no effect when the
+ * <strong>Note:</strong> Calling this method has no effect when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
- * @param shown True if the calendar view is to be shown.
+ * @param shown {@code true} to show the calendar view, {@code false} to
+ * hide it
*/
public void setCalendarViewShown(boolean shown) {
mDelegate.setCalendarViewShown(shown);
}
/**
- * Gets whether the spinners are shown.
+ * Returns whether the spinners are shown.
+ * <p>
+ * <strong>Note:</strong> his method returns {@code false} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @return True if the spinners are shown.
+ * @return {@code true} if the spinners are shown
*/
public boolean getSpinnersShown() {
return mDelegate.getSpinnersShown();
@@ -395,8 +404,13 @@ public class DatePicker extends FrameLayout {
/**
* Sets whether the spinners are shown.
+ * <p>
+ * Calling this method has no effect when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @param shown True if the spinners are to be shown.
+ * @param shown {@code true} to show the spinners, {@code false} to hide
+ * them
*/
public void setSpinnersShown(boolean shown) {
mDelegate.setSpinnersShown(shown);
@@ -489,15 +503,14 @@ public class DatePicker extends FrameLayout {
mDelegator = delegator;
mContext = context;
- // initialization based on locale
setCurrentLocale(Locale.getDefault());
}
protected void setCurrentLocale(Locale locale) {
- if (locale.equals(mCurrentLocale)) {
- return;
+ if (!locale.equals(mCurrentLocale)) {
+ mCurrentLocale = locale;
+ onLocaleChanged(locale);
}
- mCurrentLocale = locale;
}
@Override
@@ -510,6 +523,10 @@ public class DatePicker extends FrameLayout {
mValidationCallback.onValidationChanged(valid);
}
}
+
+ protected void onLocaleChanged(Locale locale) {
+ // Stub.
+ }
}
/**
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 0e3ec7f..7b8a979 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -26,90 +27,84 @@ import android.os.Parcelable;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.StateSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
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;
import com.android.internal.R;
-import com.android.internal.widget.AccessibleDateAnimator;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.HashSet;
import java.util.Locale;
/**
* A delegate for picking up a date (day / month / year).
*/
-class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
- View.OnClickListener, DatePickerController {
+class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
+
private static final int USE_LOCALE = 0;
private static final int UNINITIALIZED = -1;
- private static final int MONTH_AND_DAY_VIEW = 0;
- private static final int YEAR_VIEW = 1;
+ private static final int VIEW_MONTH_DAY = 0;
+ private static final int VIEW_YEAR = 1;
private static final int DEFAULT_START_YEAR = 1900;
private static final int DEFAULT_END_YEAR = 2100;
private static final int ANIMATION_DURATION = 300;
- private static final int MONTH_INDEX = 0;
- private static final int DAY_INDEX = 1;
- private static final int YEAR_INDEX = 2;
+ public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
- private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+ public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+ com.android.internal.R.attr.disabledAlpha};
- private TextView mDayOfWeekView;
+ private SimpleDateFormat mYearFormat;
+ private SimpleDateFormat mMonthDayFormat;
- /** Layout that contains the current month, day, and year. */
- private LinearLayout mMonthDayYearLayout;
+ // Top-level container.
+ private ViewGroup mContainer;
- /** Clickable layout that contains the current day and year. */
- private LinearLayout mMonthAndDayLayout;
+ // Header views.
+ private TextView mHeaderYear;
+ private TextView mHeaderMonthDay;
- private TextView mHeaderMonthTextView;
- private TextView mHeaderDayOfMonthTextView;
- private TextView mHeaderYearTextView;
+ // Picker views.
+ private ViewAnimator mAnimator;
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
- private boolean mIsEnabled = true;
-
// Accessibility strings.
- private String mDayPickerDescription;
private String mSelectDay;
- private String mYearPickerDescription;
private String mSelectYear;
- private AccessibleDateAnimator mAnimator;
-
private DatePicker.OnDateChangedListener mDateChangedListener;
private int mCurrentView = UNINITIALIZED;
- private Calendar mCurrentDate;
- private Calendar mTempDate;
- private Calendar mMinDate;
- private Calendar mMaxDate;
+ private final Calendar mCurrentDate;
+ private final Calendar mTempDate;
+ private final Calendar mMinDate;
+ private final Calendar mMaxDate;
private int mFirstDayOfWeek = USE_LOCALE;
- private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
-
public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
- final Locale locale = Locale.getDefault();
- mMinDate = getCalendarForLocale(mMinDate, locale);
- mMaxDate = getCalendarForLocale(mMaxDate, locale);
- mTempDate = getCalendarForLocale(mMaxDate, locale);
- mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+ final Locale locale = mCurrentLocale;
+ mCurrentDate = Calendar.getInstance(locale);
+ mTempDate = Calendar.getInstance(locale);
+ mMinDate = Calendar.getInstance(locale);
+ mMaxDate = Calendar.getInstance(locale);
mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
@@ -120,71 +115,64 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
final int layoutResourceId = a.getResourceId(
- R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
- final View mainView = inflater.inflate(layoutResourceId, null);
- mDelegator.addView(mainView);
+ R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
- mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+ // Set up and attach container.
+ mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
- // Layout that contains the current date and day name header.
- final LinearLayout dateLayout = (LinearLayout) mainView.findViewById(
- R.id.day_picker_selector_layout);
- mMonthDayYearLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_day_year_layout);
- mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_and_day_layout);
- mMonthAndDayLayout.setOnClickListener(this);
- mHeaderMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
- mHeaderDayOfMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
- mHeaderYearTextView = (TextView) mainView.findViewById(R.id.date_picker_year);
- mHeaderYearTextView.setOnClickListener(this);
+ // Set up header views.
+ final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
+ mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
+ mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+ mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
+ mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
- // Obtain default highlight color from the theme.
- final int defaultHighlightColor = mHeaderYearTextView.getHighlightColor();
+ // For the sake of backwards compatibility, attempt to extract the text
+ // color from the header month text appearance. If it's set, we'll let
+ // that override the "real" header text color.
+ ColorStateList headerTextColor = null;
- // Use Theme attributes if possible
- final int dayOfWeekTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_dayOfWeekTextAppearance, 0);
- if (dayOfWeekTextAppearanceResId != 0) {
- mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId);
+ @SuppressWarnings("deprecation")
+ final int monthHeaderTextAppearance = a.getResourceId(
+ R.styleable.DatePicker_headerMonthTextAppearance, 0);
+ if (monthHeaderTextAppearance != 0) {
+ final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+ ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
+ final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+ headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+ textAppearance.recycle();
}
- mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground));
-
- dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
-
- final int monthTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerMonthTextAppearance, 0);
- if (monthTextAppearanceResId != 0) {
- mHeaderMonthTextView.setTextAppearance(context, monthTextAppearanceResId);
+ if (headerTextColor == null) {
+ headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
}
- final int dayOfMonthTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerDayOfMonthTextAppearance, 0);
- if (dayOfMonthTextAppearanceResId != 0) {
- mHeaderDayOfMonthTextView.setTextAppearance(context, dayOfMonthTextAppearanceResId);
+ if (headerTextColor != null) {
+ mHeaderYear.setTextColor(headerTextColor);
+ mHeaderMonthDay.setTextColor(headerTextColor);
}
- final int headerYearTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerYearTextAppearance, 0);
- if (headerYearTextAppearanceResId != 0) {
- mHeaderYearTextView.setTextAppearance(context, headerYearTextAppearanceResId);
+ // Set up header background, if available.
+ if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
+ header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
}
- mDayPickerView = new DayPickerView(mContext);
+ // Set up picker container.
+ mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
+
+ // Set up day picker view.
+ mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
- mYearPickerView = new YearPickerView(mContext);
- mYearPickerView.init(this);
+ // Set up year picker view.
+ mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
-
- final ColorStateList yearBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_yearListSelectorColor);
- mYearPickerView.setYearBackgroundColor(yearBackgroundColor);
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
final int yearTextAppearanceResId = a.getResourceId(
R.styleable.DatePicker_yearListItemTextAppearance, 0);
@@ -192,170 +180,201 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
}
- final ColorStateList calendarTextColor = a.getColorStateList(
- R.styleable.DatePicker_calendarTextColor);
- mDayPickerView.setCalendarTextColor(calendarTextColor);
+ final int yearActivatedTextAppearanceResId = a.getResourceId(
+ R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
+ if (yearActivatedTextAppearanceResId != 0) {
+ mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
+ }
- final ColorStateList calendarDayBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_calendarDayBackgroundColor);
- mDayPickerView.setCalendarDayBackgroundColor(calendarDayBackgroundColor);
+ a.recycle();
- mDayPickerDescription = res.getString(R.string.day_picker_description);
+ // Set up content descriptions.
mSelectDay = res.getString(R.string.select_day);
- mYearPickerDescription = res.getString(R.string.year_picker_description);
mSelectYear = res.getString(R.string.select_year);
- mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
- mAnimator.addView(mDayPickerView);
- mAnimator.addView(mYearPickerView);
- mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+ final Animation inAnim = new AlphaAnimation(0, 1);
+ inAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setInAnimation(inAnim);
- final Animation animation = new AlphaAnimation(0.0f, 1.0f);
- animation.setDuration(ANIMATION_DURATION);
- mAnimator.setInAnimation(animation);
+ final Animation outAnim = new AlphaAnimation(1, 0);
+ outAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setOutAnimation(outAnim);
- final Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
- animation2.setDuration(ANIMATION_DURATION);
- mAnimator.setOutAnimation(animation2);
+ // Initialize for current locale. This also initializes the date, so no
+ // need to call onDateChanged.
+ onLocaleChanged(mCurrentLocale);
- updateDisplay(false);
- setCurrentView(MONTH_AND_DAY_VIEW);
+ setCurrentView(VIEW_MONTH_DAY);
}
/**
- * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ * The legacy text color might have been poorly defined. Ensures that it
+ * has an appropriate activated state, using the selected state if one
+ * exists or modifying the default text color otherwise.
*
- * @param oldCalendar The old calendar.
- * @param locale The locale.
+ * @param color a legacy text color, or {@code null}
+ * @return a color state list with an appropriate activated state, or
+ * {@code null} if a valid activated state could not be generated
*/
- private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
- if (oldCalendar == null) {
- return Calendar.getInstance(locale);
+ @Nullable
+ private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+ if (color == null || color.hasState(R.attr.state_activated)) {
+ return color;
+ }
+
+ final int activatedColor;
+ final int defaultColor;
+ if (color.hasState(R.attr.state_selected)) {
+ activatedColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+ defaultColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED), 0);
} else {
- final long currentTimeMillis = oldCalendar.getTimeInMillis();
- Calendar newCalendar = Calendar.getInstance(locale);
- newCalendar.setTimeInMillis(currentTimeMillis);
- return newCalendar;
+ activatedColor = color.getDefaultColor();
+
+ // Generate a non-activated color using the disabled alpha.
+ final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+ final float disabledAlpha = ta.getFloat(0, 0.30f);
+ defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+ }
+
+ if (activatedColor == 0 || defaultColor == 0) {
+ // We somehow failed to obtain the colors.
+ return null;
}
+
+ final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+ final int[] colors = new int[] { activatedColor, defaultColor };
+ return new ColorStateList(stateSet, colors);
+ }
+
+ private int multiplyAlphaComponent(int color, float alphaMod) {
+ final int srcRgb = color & 0xFFFFFF;
+ final int srcAlpha = (color >> 24) & 0xFF;
+ final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+ return srcRgb | (dstAlpha << 24);
}
/**
- * Compute the array representing the order of Month / Day / Year views in their layout.
- * Will be used for I18N purpose as the order of them depends on the Locale.
+ * Listener called when the user selects a day in the day picker view.
+ */
+ private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerView view, Calendar day) {
+ mCurrentDate.setTimeInMillis(day.getTimeInMillis());
+ onDateChanged(true, true);
+ }
+ };
+
+ /**
+ * Listener called when the user selects a year in the year picker view.
*/
- private int[] getMonthDayYearIndexes(String pattern) {
- int[] result = new int[3];
+ private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
+ @Override
+ public void onYearChanged(YearPickerView view, int year) {
+ // If the newly selected month / year does not contain the
+ // currently selected day number, change the selected day number
+ // to the last day of the selected month or year.
+ // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+ // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+ final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+ final int month = mCurrentDate.get(Calendar.MONTH);
+ final int daysInMonth = getDaysInMonth(month, year);
+ if (day > daysInMonth) {
+ mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
+ }
- final String filteredPattern = pattern.replaceAll("'.*?'", "");
+ mCurrentDate.set(Calendar.YEAR, year);
+ onDateChanged(true, true);
- final int dayIndex = filteredPattern.indexOf('d');
- final int monthMIndex = filteredPattern.indexOf("M");
- final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L");
- final int yearIndex = filteredPattern.indexOf("y");
+ // Automatically switch to day picker.
+ setCurrentView(VIEW_MONTH_DAY);
+ }
+ };
- if (yearIndex < monthIndex) {
- result[YEAR_INDEX] = 0;
+ /**
+ * Listener called when the user clicks on a header item.
+ */
+ private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ tryVibrate();
- if (monthIndex < dayIndex) {
- result[MONTH_INDEX] = 1;
- result[DAY_INDEX] = 2;
- } else {
- result[MONTH_INDEX] = 2;
- result[DAY_INDEX] = 1;
- }
- } else {
- result[YEAR_INDEX] = 2;
-
- if (monthIndex < dayIndex) {
- result[MONTH_INDEX] = 0;
- result[DAY_INDEX] = 1;
- } else {
- result[MONTH_INDEX] = 1;
- result[DAY_INDEX] = 0;
+ switch (v.getId()) {
+ case R.id.date_picker_header_year:
+ setCurrentView(VIEW_YEAR);
+ break;
+ case R.id.date_picker_header_date:
+ setCurrentView(VIEW_MONTH_DAY);
+ break;
}
}
- return result;
- }
+ };
- private void updateDisplay(boolean announce) {
- if (mDayOfWeekView != null) {
- mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
- Locale.getDefault()));
+ @Override
+ protected void onLocaleChanged(Locale locale) {
+ final TextView headerYear = mHeaderYear;
+ if (headerYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
}
- // Compute indices of Month, Day and Year views
- final String bestDateTimePattern =
- DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
- final int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+ // Update the date formatter.
+ final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
+ mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
+ mYearFormat = new SimpleDateFormat("y", locale);
- // Position the Year and MonthAndDay views within the header.
- mMonthDayYearLayout.removeAllViews();
- if (viewIndices[YEAR_INDEX] == 0) {
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- } else {
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- }
+ // Update the header text.
+ onCurrentDateChanged(false);
+ }
- // Position Day and Month views within the MonthAndDay view.
- mMonthAndDayLayout.removeAllViews();
- if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- } else {
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
+ private void onCurrentDateChanged(boolean announce) {
+ if (mHeaderYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
}
- mHeaderMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
- Locale.getDefault()).toUpperCase(Locale.getDefault()));
- mHeaderDayOfMonthTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
- mHeaderYearTextView.setText(mYearFormat.format(mCurrentDate.getTime()));
+ final String year = mYearFormat.format(mCurrentDate.getTime());
+ mHeaderYear.setText(year);
- // Accessibility.
- long millis = mCurrentDate.getTimeInMillis();
- mAnimator.setDateMillis(millis);
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
- String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
- mMonthAndDayLayout.setContentDescription(monthAndDayText);
+ final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
+ mHeaderMonthDay.setText(monthDay);
+ // TODO: This should use live regions.
if (announce) {
- flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
- String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+ final long millis = mCurrentDate.getTimeInMillis();
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+ final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
mAnimator.announceForAccessibility(fullDateText);
}
}
private void setCurrentView(final int viewIndex) {
- long millis = mCurrentDate.getTimeInMillis();
-
switch (viewIndex) {
- case MONTH_AND_DAY_VIEW:
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+ case VIEW_MONTH_DAY:
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(true);
- mHeaderYearTextView.setSelected(false);
- mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+ mHeaderMonthDay.setActivated(true);
+ mHeaderYear.setActivated(false);
+ mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
mCurrentView = viewIndex;
}
- final int flags = DateUtils.FORMAT_SHOW_DATE;
- final String dayString = DateUtils.formatDateTime(mContext, millis, flags);
- mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
mAnimator.announceForAccessibility(mSelectDay);
break;
- case YEAR_VIEW:
- mYearPickerView.onDateChanged();
+ case VIEW_YEAR:
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(false);
- mHeaderYearTextView.setSelected(true);
- mAnimator.setDisplayedChild(YEAR_VIEW);
+ mHeaderMonthDay.setActivated(false);
+ mHeaderYear.setActivated(true);
+ mAnimator.setDisplayedChild(VIEW_YEAR);
mCurrentView = viewIndex;
}
- final CharSequence yearString = mYearFormat.format(millis);
- mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
mAnimator.announceForAccessibility(mSelectYear);
break;
}
@@ -383,20 +402,18 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
}
private void onDateChanged(boolean fromUser, boolean callbackToClient) {
+ final int year = mCurrentDate.get(Calendar.YEAR);
+
if (callbackToClient && mDateChangedListener != null) {
- final int year = mCurrentDate.get(Calendar.YEAR);
final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
}
- for (OnDateChangedListener listener : mListeners) {
- listener.onDateChanged();
- }
-
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setYear(year);
- updateDisplay(fromUser);
+ onCurrentDateChanged(fromUser);
if (fromUser) {
tryVibrate();
@@ -477,15 +494,12 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
@Override
public void setEnabled(boolean enabled) {
- mMonthAndDayLayout.setEnabled(enabled);
- mHeaderYearTextView.setEnabled(enabled);
- mAnimator.setEnabled(enabled);
- mIsEnabled = enabled;
+ mContainer.setEnabled(false);
}
@Override
public boolean isEnabled() {
- return mIsEnabled;
+ return mContainer.isEnabled();
}
@Override
@@ -516,8 +530,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
@Override
public void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("y", newConfig.locale);
- mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+ setCurrentLocale(newConfig.locale);
}
@Override
@@ -529,9 +542,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
int listPosition = -1;
int listPositionOffset = -1;
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
+ if (mCurrentView == VIEW_MONTH_DAY) {
listPosition = mDayPickerView.getMostVisiblePosition();
- } else if (mCurrentView == YEAR_VIEW) {
+ } else if (mCurrentView == VIEW_YEAR) {
listPosition = mYearPickerView.getFirstVisiblePosition();
listPositionOffset = mYearPickerView.getFirstPositionOffset();
}
@@ -544,20 +557,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
+ // TODO: Move instance state into DayPickerView, YearPickerView.
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
mCurrentView = ss.getCurrentView();
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
- updateDisplay(false);
+ onCurrentDateChanged(false);
setCurrentView(mCurrentView);
final int listPosition = ss.getListPosition();
if (listPosition != -1) {
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
- mDayPickerView.postSetSelection(listPosition);
- } else if (mCurrentView == YEAR_VIEW) {
- mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+ if (mCurrentView == VIEW_MONTH_DAY) {
+ mDayPickerView.setCurrentItem(listPosition);
+ } else if (mCurrentView == VIEW_YEAR) {
+ final int listPositionOffset = ss.getListPositionOffset();
+ mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
}
}
}
@@ -577,28 +592,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
return DatePicker.class.getName();
}
- @Override
- public void onYearSelected(int year) {
- adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
- mCurrentDate.set(Calendar.YEAR, year);
- onDateChanged(true, true);
-
- // Auto-advance to month and day view.
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
-
- // If the newly selected month / year does not contain the currently selected day number,
- // change the selected day number to the last day of the selected month or year.
- // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
- // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
- private void adjustDayInMonthIfNeeded(int month, int year) {
- int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
- int daysInMonth = getDaysInMonth(month, year);
- if (day > daysInMonth) {
- mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
- }
- }
-
public static int getDaysInMonth(int month, int year) {
switch (month) {
case Calendar.JANUARY:
@@ -621,43 +614,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
}
}
- @Override
- public void registerOnDateChangedListener(OnDateChangedListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public Calendar getSelectedDay() {
- return mCurrentDate;
- }
-
- @Override
- public void tryVibrate() {
+ private void tryVibrate() {
mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
}
- @Override
- public void onClick(View v) {
- tryVibrate();
- if (v.getId() == R.id.date_picker_year) {
- setCurrentView(YEAR_VIEW);
- } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
- }
-
- /**
- * Listener called when the user selects a day in the day picker view.
- */
- private final DayPickerView.OnDaySelectedListener
- mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() {
- @Override
- public void onDaySelected(DayPickerView view, Calendar day) {
- mCurrentDate.setTimeInMillis(day.getTimeInMillis());
- onDateChanged(true, true);
- }
- };
-
/**
* Class for managing state storing/restoring.
*/
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
new file mode 100644
index 0000000..4f9f09e
--- /dev/null
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -0,0 +1,283 @@
+/*
+ * 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.PagerAdapter;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SimpleMonthView.OnDayClickListener;
+
+import java.util.Calendar;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class DayPickerAdapter extends PagerAdapter {
+ private static final int MONTHS_IN_YEAR = 12;
+
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
+
+ private final SparseArray<SimpleMonthView> mItems = new SparseArray<>();
+
+ private Calendar mSelectedDay = Calendar.getInstance();
+
+ private int mMonthTextAppearance;
+ private int mDayOfWeekTextAppearance;
+ private int mDayTextAppearance;
+
+ private ColorStateList mCalendarTextColor;
+ private ColorStateList mDaySelectorColor;
+ private ColorStateList mDayHighlightColor;
+
+ private OnDaySelectedListener mOnDaySelectedListener;
+
+ private int mFirstDayOfWeek;
+
+ public DayPickerAdapter(Context context) {
+ final TypedArray ta = context.obtainStyledAttributes(new int[] {
+ com.android.internal.R.attr.colorControlHighlight});
+ mDayHighlightColor = ta.getColorStateList(0);
+ ta.recycle();
+ }
+
+ public void setRange(Calendar min, Calendar max) {
+ mMinDate.setTimeInMillis(min.getTimeInMillis());
+ mMaxDate.setTimeInMillis(max.getTimeInMillis());
+
+ // Positions are now invalid, clear everything and start over.
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ mFirstDayOfWeek = weekStart;
+
+ // Update displayed views.
+ final int count = mItems.size();
+ for (int i = 0; i < count; i++) {
+ final SimpleMonthView monthView = mItems.valueAt(i);
+ monthView.setFirstDayOfWeek(weekStart);
+ }
+ }
+
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ /**
+ * Sets the selected day.
+ *
+ * @param day the selected day
+ */
+ public void setSelectedDay(Calendar day) {
+ final int oldPosition = getPositionForDay(mSelectedDay);
+ final int newPosition = getPositionForDay(day);
+
+ // Clear the old position if necessary.
+ if (oldPosition != newPosition) {
+ final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+ if (oldMonthView != null) {
+ oldMonthView.setSelectedDay(-1);
+ }
+ }
+
+ // Set the new position.
+ final SimpleMonthView newMonthView = mItems.get(newPosition, null);
+ if (newMonthView != null) {
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ newMonthView.setSelectedDay(dayOfMonth);
+ }
+
+ mSelectedDay = day;
+ }
+
+ /**
+ * Sets the listener to call when the user selects a day.
+ *
+ * @param listener The listener to call.
+ */
+ public void setOnDaySelectedListener(OnDaySelectedListener listener) {
+ mOnDaySelectedListener = listener;
+ }
+
+ void setCalendarTextColor(ColorStateList calendarTextColor) {
+ mCalendarTextColor = calendarTextColor;
+ }
+
+ void setDaySelectorColor(ColorStateList selectorColor) {
+ mDaySelectorColor = selectorColor;
+ }
+
+ void setMonthTextAppearance(int resId) {
+ mMonthTextAppearance = resId;
+ }
+
+ void setDayOfWeekTextAppearance(int resId) {
+ mDayOfWeekTextAppearance = resId;
+ }
+
+ int getDayOfWeekTextAppearance() {
+ return mDayOfWeekTextAppearance;
+ }
+
+ void setDayTextAppearance(int resId) {
+ mDayTextAppearance = resId;
+ }
+
+ int getDayTextAppearance() {
+ return mDayTextAppearance;
+ }
+
+ @Override
+ public int getCount() {
+ final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+ final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+ return diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ private int getMonthForPosition(int position) {
+ return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH);
+ }
+
+ private int getYearForPosition(int position) {
+ return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
+ }
+
+ private int getPositionForDay(Calendar day) {
+ 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;
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final SimpleMonthView v = new SimpleMonthView(container.getContext());
+ v.setOnDayClickListener(mOnDayClickListener);
+ v.setMonthTextAppearance(mMonthTextAppearance);
+ v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
+ v.setDayTextAppearance(mDayTextAppearance);
+
+ if (mDaySelectorColor != null) {
+ v.setDaySelectorColor(mDaySelectorColor);
+ }
+
+ if (mDayHighlightColor != null) {
+ v.setDayHighlightColor(mDayHighlightColor);
+ }
+
+ if (mCalendarTextColor != null) {
+ v.setMonthTextColor(mCalendarTextColor);
+ v.setDayOfWeekTextColor(mCalendarTextColor);
+ v.setDayTextColor(mCalendarTextColor);
+ }
+
+ final int month = getMonthForPosition(position);
+ final int year = getYearForPosition(position);
+
+ final int selectedDay;
+ if (mSelectedDay.get(Calendar.MONTH) == month) {
+ selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+ } else {
+ selectedDay = -1;
+ }
+
+ final int enabledDayRangeStart;
+ if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeStart = 1;
+ }
+
+ final int enabledDayRangeEnd;
+ if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeEnd = 31;
+ }
+
+ v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
+ enabledDayRangeStart, enabledDayRangeEnd);
+
+ mItems.put(position, v);
+
+ container.addView(v);
+
+ return v;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView(mItems.get(position));
+
+ mItems.remove(position);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ final int index = mItems.indexOfValue((SimpleMonthView) object);
+ if (index < 0) {
+ return mItems.keyAt(index);
+ }
+ return -1;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ final SimpleMonthView v = mItems.get(position);
+ if (v != null) {
+ return v.getTitle();
+ }
+ return null;
+ }
+
+ private boolean isCalendarInRange(Calendar value) {
+ return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
+ }
+
+ private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
+ @Override
+ public void onDayClick(SimpleMonthView view, Calendar day) {
+ if (day != null && isCalendarInRange(day)) {
+ setSelectedDay(day);
+
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+ }
+ }
+ }
+ };
+
+ public interface OnDaySelectedListener {
+ public void onDaySelected(DayPickerAdapter view, Calendar day);
+ }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 65af45d..a7ae926 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -16,87 +16,177 @@
package android.widget;
+import com.android.internal.widget.ViewPager;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import java.text.SimpleDateFormat;
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 ListView implements AbsListView.OnScrollListener {
- private static final String TAG = "DayPickerView";
+class DayPickerView extends ViewPager {
+ private static final int DEFAULT_START_YEAR = 1900;
+ private static final int DEFAULT_END_YEAR = 2100;
- // How long the GoTo fling animation should last
- private static final int GOTO_SCROLL_DURATION = 250;
+ private final Calendar mSelectedDay = Calendar.getInstance();
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
- // How long to wait after receiving an onScrollStateChanged notification before acting on it
- private static final int SCROLL_CHANGE_DELAY = 40;
+ private final DayPickerAdapter mAdapter;
- // so that the top line will be under the separator
- private static final int LIST_TOP_OFFSET = -1;
+ /** Temporary calendar used for date calculations. */
+ private Calendar mTempCalendar;
- private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext());
+ private OnDaySelectedListener mOnDaySelectedListener;
- private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
+ public DayPickerView(Context context) {
+ this(context, null);
+ }
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+ public DayPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.calendarViewStyle);
+ }
- // highlighted time
- private Calendar mSelectedDay = Calendar.getInstance();
- private Calendar mTempDay = Calendar.getInstance();
- private Calendar mMinDate = Calendar.getInstance();
- private Calendar mMaxDate = Calendar.getInstance();
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
- private Calendar mTempCalendar;
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
- private OnDaySelectedListener mOnDaySelectedListener;
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
- // which month should be displayed/highlighted [0-11]
- private int mCurrentMonthDisplayed;
- // used for tracking what state listview is in
- private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
- // used for tracking what state listview is in
- private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+ final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
- private boolean mPerformingScroll;
+ final String minDate = a.getString(R.styleable.CalendarView_minDate);
+ final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
- public DayPickerView(Context context) {
- super(context);
+ final int monthTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_monthTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Month);
+ final int dayOfWeekTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_DayOfWeek);
+ final int dayTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Day);
+
+ final ColorStateList daySelectorColor = a.getColorStateList(
+ R.styleable.CalendarView_daySelectorColor);
+
+ a.recycle();
+
+ // Set up adapter.
+ mAdapter = new DayPickerAdapter(context);
+ mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
+ mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
+ mAdapter.setDayTextAppearance(dayTextAppearanceResId);
+ mAdapter.setDaySelectorColor(daySelectorColor);
setAdapter(mAdapter);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- setDrawSelectorOnTop(false);
- setUpListView();
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ // Set up min and max dates.
+ final Calendar tempDate = Calendar.getInstance();
+ if (!CalendarView.parseDate(minDate, tempDate)) {
+ tempDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
+ }
+ final long minDateMillis = tempDate.getTimeInMillis();
+
+ if (!CalendarView.parseDate(maxDate, tempDate)) {
+ tempDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
+ }
+ final long maxDateMillis = tempDate.getTimeInMillis();
+
+ if (maxDateMillis < minDateMillis) {
+ throw new IllegalArgumentException("maxDate must be >= minDate");
+ }
+
+ final long setDateMillis = MathUtils.constrain(
+ System.currentTimeMillis(), minDateMillis, maxDateMillis);
- mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
+ setFirstDayOfWeek(firstDayOfWeek);
+ setMinDate(minDateMillis);
+ setMaxDate(maxDateMillis);
+ setDate(setDateMillis, false);
+
+ // Proxy selection callbacks to our own listener.
+ mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
+ }
+ }
+ });
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ mAdapter.setDayOfWeekTextAppearance(resId);
+ }
+
+ public int getDayOfWeekTextAppearance() {
+ return mAdapter.getDayOfWeekTextAppearance();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ mAdapter.setDayTextAppearance(resId);
+ }
+
+ public int getDayTextAppearance() {
+ return mAdapter.getDayTextAppearance();
}
/**
* Sets the currently selected date to the specified timestamp. Jumps
* immediately to the new date. To animate to the new date, use
- * {@link #setDate(long, boolean, boolean)}.
+ * {@link #setDate(long, boolean)}.
*
- * @param timeInMillis
+ * @param timeInMillis the target day in milliseconds
*/
public void setDate(long timeInMillis) {
- setDate(timeInMillis, false, true);
+ setDate(timeInMillis, false);
}
- public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
- goTo(timeInMillis, animate, true, forceScroll);
+ /**
+ * Sets the currently selected date to the specified timestamp. Jumps
+ * immediately to the new date, optionally animating the transition.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ */
+ public void setDate(long timeInMillis, boolean animate) {
+ setDate(timeInMillis, animate, true);
+ }
+
+ /**
+ * Moves to the month containing the specified day, optionally setting the
+ * day as selected.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ * @param setSelected whether to set the specified day as selected
+ */
+ private void setDate(long timeInMillis, boolean animate, boolean setSelected) {
+ // Set the selected day
+ if (setSelected) {
+ mSelectedDay.setTimeInMillis(timeInMillis);
+ }
+
+ final int position = getPositionFromDay(timeInMillis);
+ if (position != getCurrentItem()) {
+ setCurrentItem(position, animate);
+ }
}
public long getDate() {
@@ -137,7 +227,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
// Changing the min/max date changes the selection position since we
// don't really have stable IDs. Jumps immediately to the new position.
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ setDate(mSelectedDay.getTimeInMillis(), false, false);
}
/**
@@ -149,30 +239,9 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
mOnDaySelectedListener = listener;
}
- /*
- * Sets all the required fields for the list view. Override this method to
- * set a different list view behavior.
- */
- private void setUpListView() {
- // Transparent background on scroll
- setCacheColorHint(0);
- // No dividers
- setDivider(null);
- // Items are clickable
- setItemsCanFocus(true);
- // The thumb gets in the way, so disable it
- setFastScrollEnabled(false);
- setVerticalScrollBarEnabled(false);
- setOnScrollListener(this);
- setFadingEdgeLength(0);
- // Make the scrolling behavior nicer
- setFriction(ViewConfiguration.getScrollFriction());
- }
-
private int getDiffMonths(Calendar start, Calendar end) {
final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
- final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
- return diffMonths;
+ return end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
}
private int getPositionFromDay(long timeInMillis) {
@@ -190,366 +259,13 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
}
/**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param day The day to move to
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location
- * @param setSelected Whether to set the given time as selected
- * @param forceScroll Whether to recenter even if the time is already
- * visible
- * @return Whether or not the view animated to the new location
- */
- private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
-
- // Set the selected day
- if (setSelected) {
- mSelectedDay.setTimeInMillis(day);
- }
-
- mTempDay.setTimeInMillis(day);
- final int position = getPositionFromDay(day);
-
- View child;
- int i = 0;
- int top = 0;
- // Find a child that's completely in the view
- do {
- child = getChildAt(i++);
- if (child == null) {
- break;
- }
- top = child.getTop();
- } while (top < 0);
-
- // Compute the first and last position visible
- int selectedPosition;
- if (child != null) {
- selectedPosition = getPositionForView(child);
- } else {
- selectedPosition = 0;
- }
-
- if (setSelected) {
- mAdapter.setSelectedDay(mSelectedDay);
- }
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position != selectedPosition || forceScroll) {
- setMonthDisplayed(mTempDay);
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- smoothScrollToPositionFromTop(
- position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
- return true;
- } else {
- postSetSelection(position);
- }
- } else if (setSelected) {
- setMonthDisplayed(mSelectedDay);
- }
- return false;
- }
-
- public void postSetSelection(final int position) {
- clearFocus();
- post(new Runnable() {
-
- @Override
- public void run() {
- setSelection(position);
- }
- });
- onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
- }
-
- /**
- * Updates the title and selected month if the view has moved to a new
- * month.
- */
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- mPreviousScrollState = mCurrentScrollState;
- }
-
- /**
- * Sets the month displayed at the top of this view based on time. Override
- * to add custom events when the title is changed.
- */
- protected void setMonthDisplayed(Calendar date) {
- if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
- mCurrentMonthDisplayed = date.get(Calendar.MONTH);
- invalidateViews();
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // use a post to prevent re-entering onScrollStateChanged before it
- // exits
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mAdapter.setCalendarTextColor(colors);
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mAdapter.setCalendarDayBackgroundColor(dayBackgroundColor);
- }
-
- void setCalendarTextAppearance(int resId) {
- mAdapter.setCalendarTextAppearance(resId);
- }
-
- protected class ScrollStateRunnable implements Runnable {
- private int mNewState;
- private View mParent;
-
- ScrollStateRunnable(View view) {
- mParent = view;
- }
-
- /**
- * Sets up the runnable with a short delay in case the scroll state
- * immediately changes again.
- *
- * @param view The list view that changed state
- * @param scrollState The new state it changed to
- */
- public void doScrollStateChange(AbsListView view, int scrollState) {
- mParent.removeCallbacks(this);
- mNewState = scrollState;
- mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- @Override
- public void run() {
- mCurrentScrollState = mNewState;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG,
- "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
- }
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- mPreviousScrollState = mNewState;
- int i = 0;
- View child = getChildAt(i);
- while (child != null && child.getBottom() <= 0) {
- child = getChildAt(++i);
- }
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int firstPosition = getFirstVisiblePosition();
- int lastPosition = getLastVisiblePosition();
- boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
- final int top = child.getTop();
- final int bottom = child.getBottom();
- final int midpoint = getHeight() / 2;
- if (scroll && top < LIST_TOP_OFFSET) {
- if (bottom > midpoint) {
- smoothScrollBy(top, GOTO_SCROLL_DURATION);
- } else {
- smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
- }
- }
- } else {
- mPreviousScrollState = mNewState;
- }
- }
- }
-
- /**
* Gets the position of the view that is most prominently displayed within the list view.
*/
public int getMostVisiblePosition() {
- final int firstPosition = getFirstVisiblePosition();
- final int height = getHeight();
-
- int maxDisplayedHeight = 0;
- int mostVisibleIndex = 0;
- int i=0;
- int bottom = 0;
- while (bottom < height) {
- View child = getChildAt(i);
- if (child == null) {
- break;
- }
- bottom = child.getBottom();
- int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
- if (displayedHeight > maxDisplayedHeight) {
- mostVisibleIndex = i;
- maxDisplayedHeight = displayedHeight;
- }
- i++;
- }
- return firstPosition + mostVisibleIndex;
- }
-
- /**
- * Attempts to return the date that has accessibility focus.
- *
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus.
- */
- private Calendar findAccessibilityFocus() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
- if (focus != null) {
- return focus;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Attempts to restore accessibility focus to a given date. No-op if
- * {@code day} is {@code null}.
- *
- * @param day The date that should receive accessibility focus
- * @return {@code true} if focus was restored
- */
- private boolean restoreAccessibilityFocus(Calendar day) {
- if (day == null) {
- return false;
- }
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- protected void layoutChildren() {
- final Calendar focusedDay = findAccessibilityFocus();
- super.layoutChildren();
- if (mPerformingScroll) {
- mPerformingScroll = false;
- } else {
- restoreAccessibilityFocus(focusedDay);
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
- }
-
- /** @hide */
- @Override
- public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
- super.onInitializeAccessibilityEventInternal(event);
- event.setItemCount(-1);
- }
-
- private String getMonthAndYearString(Calendar day) {
- final StringBuilder sbuf = new StringBuilder();
- sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
- sbuf.append(" ");
- sbuf.append(mYearFormat.format(day.getTime()));
- return sbuf.toString();
- }
-
- /**
- * Necessary for accessibility, to ensure we support "scrolling" forward and backward
- * in the month list.
- *
- * @hide
- */
- @Override
- public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfoInternal(info);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
- }
-
- /**
- * When scroll forward/backward events are received, announce the newly scrolled-to month.
- *
- * @hide
- */
- @Override
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
- action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- return super.performAccessibilityActionInternal(action, arguments);
- }
-
- // Figure out what month is showing.
- final int firstVisiblePosition = getFirstVisiblePosition();
- final int month = firstVisiblePosition % 12;
- final int year = firstVisiblePosition / 12 + mMinDate.get(Calendar.YEAR);
- final Calendar day = Calendar.getInstance();
- day.set(year, month, 1);
-
- // Scroll either forward or backward one month.
- if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
- day.add(Calendar.MONTH, 1);
- if (day.get(Calendar.MONTH) == 12) {
- day.set(Calendar.MONTH, 0);
- day.add(Calendar.YEAR, 1);
- }
- } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- View firstVisibleView = getChildAt(0);
- // If the view is fully visible, jump one month back. Otherwise, we'll just jump
- // to the first day of first visible month.
- if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
- // There's an off-by-one somewhere, so the top of the first visible item will
- // actually be -1 when it's at the exact top.
- day.add(Calendar.MONTH, -1);
- if (day.get(Calendar.MONTH) == -1) {
- day.set(Calendar.MONTH, 11);
- day.add(Calendar.YEAR, -1);
- }
- }
- }
-
- // Go to that month.
- announceForAccessibility(getMonthAndYearString(day));
- goTo(day.getTimeInMillis(), true, false, true);
- mPerformingScroll = true;
- return true;
+ return getCurrentItem();
}
public interface OnDaySelectedListener {
public void onDaySelected(DayPickerView view, Calendar day);
}
-
- private final SimpleMonthAdapter.OnDaySelectedListener
- mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() {
- @Override
- public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) {
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
- }
- }
- };
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d93b212..6e24837 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -77,14 +77,14 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
-import android.view.RenderNode;
+import android.view.DisplayListCanvas;
import android.view.DragEvent;
import android.view.Gravity;
-import android.view.HardwareCanvas;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.RenderNode;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnClickListener;
@@ -236,12 +236,17 @@ public class Editor {
}
ParcelableParcel saveInstanceState() {
- // For now there is only undo state.
- return (ParcelableParcel) mUndoManager.saveInstanceState();
+ ParcelableParcel state = new ParcelableParcel(getClass().getClassLoader());
+ Parcel parcel = state.getParcel();
+ mUndoManager.saveInstanceState(parcel);
+ mUndoInputFilter.saveInstanceState(parcel);
+ return state;
}
void restoreInstanceState(ParcelableParcel state) {
- mUndoManager.restoreInstanceState(state);
+ Parcel parcel = state.getParcel();
+ mUndoManager.restoreInstanceState(parcel, state.getClassLoader());
+ mUndoInputFilter.restoreInstanceState(parcel);
// Re-associate this object as the owner of undo state.
mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
}
@@ -309,7 +314,7 @@ public class Editor {
mTextView.setHasTransientState(false);
// We had an active selection from before, start the selection mode.
- startSelectionActionMode();
+ startSelectionActionModeWithSelection();
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
@@ -976,14 +981,15 @@ public class Editor {
}
public boolean performLongClick(boolean handled) {
- // Long press in empty space moves cursor and shows the Paste affordance if available.
+ // Long press in empty space moves cursor and starts the selection action mode.
if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
mInsertionControllerEnabled) {
final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY);
stopSelectionActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
- getInsertionController().showWithActionPopup();
+ getInsertionController().show();
+ startSelectionActionModeWithoutSelection();
handled = true;
}
@@ -995,18 +1001,19 @@ public class Editor {
CharSequence selectedText = mTextView.getTransformedText(start, end);
ClipData data = ClipData.newPlainText(null, selectedText);
DragLocalState localState = new DragLocalState(mTextView, start, end);
- mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0);
+ mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState,
+ View.DRAG_FLAG_GLOBAL);
stopSelectionActionMode();
} else {
stopSelectionActionMode();
- startSelectionActionMode();
+ startSelectionActionModeWithSelection();
}
handled = true;
}
// Start a new selection
if (!handled) {
- handled = startSelectionActionMode();
+ handled = startSelectionActionModeWithSelection();
}
return handled;
@@ -1508,18 +1515,18 @@ public class Editor {
// Rebuild display list if it is invalid
if (blockDisplayListIsInvalid) {
- final HardwareCanvas hardwareCanvas = blockDisplayList.start(
+ final DisplayListCanvas displayListCanvas = blockDisplayList.start(
right - left, bottom - top);
try {
// drawText is always relative to TextView's origin, this translation
// brings this range of text back to the top left corner of the viewport
- hardwareCanvas.translate(-left, -top);
- layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine);
+ displayListCanvas.translate(-left, -top);
+ layout.drawText(displayListCanvas, blockBeginLine, blockEndLine);
mTextDisplayLists[blockIndex].isDirty = false;
// No need to untranslate, previous context is popped after
// drawDisplayList
} finally {
- blockDisplayList.end(hardwareCanvas);
+ blockDisplayList.end(displayListCanvas);
// Same as drawDisplayList below, handled by our TextView's parent
blockDisplayList.setClipToBounds(false);
}
@@ -1530,7 +1537,7 @@ public class Editor {
blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
}
- ((HardwareCanvas) canvas).drawRenderNode(blockDisplayList,
+ ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList,
0 /* no child clipping, our TextView parent enforces it */);
endOfPreviousBlock = blockEndLine;
@@ -1657,7 +1664,21 @@ public class Editor {
/**
* @return true if the selection mode was actually started.
*/
- boolean startSelectionActionMode() {
+ private boolean startSelectionActionModeWithoutSelection() {
+ if (mSelectionActionMode != null) {
+ // Selection action mode is already started
+ // TODO: revisit invocations to minimize this case.
+ return false;
+ }
+ ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
+ mSelectionActionMode = mTextView.startActionMode(actionModeCallback);
+ return mSelectionActionMode != null;
+ }
+
+ /**
+ * @return true if the selection mode was actually started.
+ */
+ boolean startSelectionActionModeWithSelection() {
if (mSelectionActionMode != null) {
// Selection action mode is already started
return false;
@@ -3254,7 +3275,7 @@ public class Editor {
if (isTopLeftVisible || isBottomRightVisible) {
characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
}
- if (!isTopLeftVisible || !isTopLeftVisible) {
+ if (!isTopLeftVisible || !isBottomRightVisible) {
characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
}
if (isRtl) {
@@ -3320,18 +3341,14 @@ public class Editor {
private float mIdealVerticalOffset;
// Parent's (TextView) previous position in window
private int mLastParentX, mLastParentY;
- // Transient action popup window for Paste and Replace actions
- protected ActionPopupWindow mActionPopupWindow;
// Previous text character offset
private int mPreviousOffset = -1;
// Previous text character offset
private boolean mPositionHasChanged = true;
- // Used to delay the appearance of the action popup window
- private Runnable mActionPopupShower;
// Minimum touch target size for handles
private int mMinSize;
// Indicates the line of text that the handle is on.
- protected int mLine = -1;
+ protected int mPrevLine = -1;
public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(mTextView.getContext());
@@ -3429,8 +3446,6 @@ public class Editor {
// Make sure the offset is always considered new, even when focusing at same position
mPreviousOffset = -1;
positionAtCursorOffset(getCurrentCursorOffset(), false);
-
- hideActionPopupWindow();
}
protected void dismiss() {
@@ -3445,31 +3460,6 @@ public class Editor {
getPositionListener().removeSubscriber(this);
}
- void showActionPopupWindow(int delay) {
- if (mActionPopupWindow == null) {
- mActionPopupWindow = new ActionPopupWindow();
- }
- if (mActionPopupShower == null) {
- mActionPopupShower = new Runnable() {
- public void run() {
- mActionPopupWindow.show();
- }
- };
- } else {
- mTextView.removeCallbacks(mActionPopupShower);
- }
- mTextView.postDelayed(mActionPopupShower, delay);
- }
-
- protected void hideActionPopupWindow() {
- if (mActionPopupShower != null) {
- mTextView.removeCallbacks(mActionPopupShower);
- }
- if (mActionPopupWindow != null) {
- mActionPopupWindow.hide();
- }
- }
-
public boolean isShowing() {
return mContainer.isShowing();
}
@@ -3509,7 +3499,7 @@ public class Editor {
addPositionToTouchUpFilter(offset);
}
final int line = layout.getLineForOffset(offset);
- mLine = line;
+ mPrevLine = line;
mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
getHorizontalOffset() + getCursorOffset());
@@ -3670,20 +3660,16 @@ public class Editor {
return mIsDragging;
}
- void onHandleMoved() {
- hideActionPopupWindow();
- }
+ void onHandleMoved() {}
- public void onDetached() {
- hideActionPopupWindow();
- }
+ public void onDetached() {}
}
private class InsertionHandleView extends HandleView {
private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
- // Used to detect taps on the insertion handle, which will affect the ActionPopupWindow
+ // Used to detect taps on the insertion handle, which will affect the selection action mode
private float mDownPositionX, mDownPositionY;
private Runnable mHider;
@@ -3698,17 +3684,12 @@ public class Editor {
final long durationSinceCutOrCopy =
SystemClock.uptimeMillis() - TextView.LAST_CUT_OR_COPY_TIME;
if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
- showActionPopupWindow(0);
+ startSelectionActionModeWithoutSelection();
}
hideAfterDelay();
}
- public void showWithActionPopup() {
- show();
- showActionPopupWindow(0);
- }
-
private void hideAfterDelay() {
if (mHider == null) {
mHider = new Runnable() {
@@ -3770,11 +3751,11 @@ public class Editor {
final int touchSlop = viewConfiguration.getScaledTouchSlop();
if (distanceSquared < touchSlop * touchSlop) {
- if (mActionPopupWindow != null && mActionPopupWindow.isShowing()) {
- // Tapping on the handle dismisses the displayed action popup
- mActionPopupWindow.hide();
+ // Tapping on the handle toggles the selection action mode.
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.finish();
} else {
- showWithActionPopup();
+ startSelectionActionModeWithoutSelection();
}
}
}
@@ -3858,19 +3839,22 @@ public class Editor {
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
- int offset = trueOffset;
- boolean positionCursor = false;
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
+ boolean positionCursor = false;
+ int offset = trueOffset;
int end = getWordEnd(offset, true);
int start = getWordStart(offset);
if (offset < mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine < mLine) {
+ if (!mInWord || currLine < mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int offsetToWord = Math.min((end - start) / 2, 2);
- if (offset <= end - offsetToWord || currLine < mLine) {
+ if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
offset = mPrevOffset;
@@ -3882,7 +3866,7 @@ public class Editor {
positionCursor = true;
} else if (offset - mTouchWordOffset > mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = end;
}
@@ -3897,7 +3881,7 @@ public class Editor {
final int selectionEnd = mTextView.getSelectionEnd();
if (offset >= selectionEnd) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
if (alteredOffset >= selectionEnd) {
// Can't pass the other drag handle.
offset = Math.max(0, selectionEnd - 1);
@@ -3909,10 +3893,6 @@ public class Editor {
}
}
- public ActionPopupWindow getActionPopupWindow() {
- return mActionPopupWindow;
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
@@ -3962,6 +3942,10 @@ public class Editor {
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
int offset = trueOffset;
boolean positionCursor = false;
@@ -3970,11 +3954,11 @@ public class Editor {
if (offset > mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine > mLine) {
+ if (!mInWord || currLine > mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int midPoint = Math.min((end - start) / 2, 2);
- if (offset >= start + midPoint || currLine > mLine) {
+ if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
offset = mPrevOffset;
@@ -3986,7 +3970,7 @@ public class Editor {
positionCursor = true;
} else if (offset + mTouchWordOffset < mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = getWordStart(offset);
}
@@ -4000,7 +3984,7 @@ public class Editor {
final int selectionStart = mTextView.getSelectionStart();
if (offset <= selectionStart) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
int length = mTextView.getText().length();
if (alteredOffset <= selectionStart) {
// Can't pass the other drag handle.
@@ -4013,10 +3997,6 @@ public class Editor {
}
}
- public void setActionPopupWindow(ActionPopupWindow actionPopupWindow) {
- mActionPopupWindow = actionPopupWindow;
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
@@ -4029,6 +4009,36 @@ public class Editor {
}
/**
+ * Checks whether selection is happening on a different line than previous and
+ * if that line only contains whitespace up to the touch location.
+ *
+ * @param prevLine The previous line the selection was on.
+ * @param currLine The current line being selected.
+ * @param offset The offset in the text where the touch occurred.
+ * @return Whether or not it was just a white space line being selected.
+ */
+ private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
+ if (prevLine == currLine) {
+ // Same line; don't care.
+ return false;
+ }
+ CharSequence text = mTextView.getText();
+ if (offset == text.length()) {
+ // No character at the last position.
+ return false;
+ }
+ int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
+ for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
+ cp = Character.codePointAt(text, i);
+ if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
+ // There are non white space chars on the line.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* A CursorController instance can be used to control a cursor in the text.
*/
private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4059,10 +4069,6 @@ public class Editor {
getHandle().show();
}
- public void showWithActionPopup() {
- getHandle().showWithActionPopup();
- }
-
public void hide() {
if (mHandle != null) {
mHandle.hide();
@@ -4112,6 +4118,8 @@ public class Editor {
private int mStartOffset = -1;
// Indicates whether the user is selecting text and using the drag accelerator.
private boolean mDragAcceleratorActive;
+ // Indicates the line of text the drag accelerator is on.
+ private int mPrevLine = -1;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -4149,11 +4157,6 @@ public class Editor {
mStartHandle.show();
mEndHandle.show();
- // Make sure both left and right handles share the same ActionPopupWindow (so that
- // moving any of the handles hides the action popup).
- mStartHandle.showActionPopupWindow(DELAY_BEFORE_REPLACE_ACTION);
- mEndHandle.setActionPopupWindow(mStartHandle.getActionPopupWindow());
-
hideInsertionPointCursorController();
}
@@ -4203,12 +4206,14 @@ public class Editor {
boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
if (stayedInArea && isPositionOnText(x, y)) {
- startSelectionActionMode();
+ startSelectionActionModeWithSelection();
mDiscardNextActionUp = true;
}
}
}
+ // New selection, reset line.
+ mPrevLine = mTextView.getLineAtCoordinate(y);
mDownPositionX = x;
mDownPositionY = y;
mGestureStayedInTapRegion = true;
@@ -4265,6 +4270,13 @@ public class Editor {
if (my > fingerOffset) my -= fingerOffset;
offset = mTextView.getOffsetForPosition(mx, my);
+ int currLine = mTextView.getLineAtCoordinate(my);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
+
+ mPrevLine = currLine;
+
// Perform the check for closeness at edge of view, if we're very close
// don't adjust the offset to be in front of the finger - otherwise the
// user can't select words at the edge.
@@ -4576,20 +4588,30 @@ public class Editor {
// Whether the current filter pass is directly caused by an end-user text edit.
private boolean mIsUserEdit;
- // Whether this is the first pass through the filter for a given end-user text edit.
- private boolean mFirstFilterPass;
+ // Whether the text field is handling an IME composition. Must be parceled in case the user
+ // rotates the screen during composition.
+ private boolean mHasComposition;
public UndoInputFilter(Editor editor) {
mEditor = editor;
}
+ public void saveInstanceState(Parcel parcel) {
+ parcel.writeInt(mIsUserEdit ? 1 : 0);
+ parcel.writeInt(mHasComposition ? 1 : 0);
+ }
+
+ public void restoreInstanceState(Parcel parcel) {
+ mIsUserEdit = parcel.readInt() != 0;
+ mHasComposition = parcel.readInt() != 0;
+ }
+
/**
* Signals that a user-triggered edit is starting.
*/
public void beginBatchEdit() {
if (DEBUG_UNDO) Log.d(TAG, "beginBatchEdit");
mIsUserEdit = true;
- mFirstFilterPass = true;
}
public void endBatchEdit() {
@@ -4610,17 +4632,67 @@ public class Editor {
return null;
}
+ // Check for and handle IME composition edits.
+ if (handleCompositionEdit(source, start, end, dstart)) {
+ return null;
+ }
+
+ // Handle keyboard edits.
+ handleKeyboardEdit(source, start, end, dest, dstart, dend);
+ return null;
+ }
+
+ /**
+ * Returns true iff the edit was handled, either because it should be ignored or because
+ * this function created an undo operation for it.
+ */
+ private boolean handleCompositionEdit(CharSequence source, int start, int end, int dstart) {
+ // Ignore edits while the user is composing.
+ if (isComposition(source)) {
+ mHasComposition = true;
+ return true;
+ }
+ final boolean hadComposition = mHasComposition;
+ mHasComposition = false;
+
+ // Check for the transition out of the composing state.
+ if (hadComposition) {
+ // If there was no text the user canceled composition. Ignore the edit.
+ if (start == end) {
+ return true;
+ }
+
+ // Otherwise the user inserted the composition.
+ String newText = TextUtils.substring(source, start, end);
+ EditOperation edit = new EditOperation(mEditor, "", dstart, newText);
+ recordEdit(edit, false /* forceMerge */);
+ return true;
+ }
+
+ // This was neither a composition event nor a transition out of composing.
+ return false;
+ }
+
+ private void handleKeyboardEdit(CharSequence source, int start, int end,
+ Spanned dest, int dstart, int dend) {
// An application may install a TextWatcher to provide additional modifications after
// the initial input filters run (e.g. a credit card formatter that adds spaces to a
// string). This results in multiple filter() calls for what the user considers to be
// a single operation. Always undo the whole set of changes in one step.
- final boolean forceMerge = !mFirstFilterPass;
- mFirstFilterPass = false;
+ final boolean forceMerge = isInTextWatcher();
// Build a new operation with all the information from this edit.
- EditOperation edit = new EditOperation(mEditor, forceMerge,
- source, start, end, dest, dstart, dend);
+ String newText = TextUtils.substring(source, start, end);
+ String oldText = TextUtils.substring(dest, dstart, dend);
+ EditOperation edit = new EditOperation(mEditor, oldText, dstart, newText);
+ recordEdit(edit, forceMerge);
+ }
+ /**
+ * Fetches the last undo operation and checks to see if a new edit should be merged into it.
+ * If forceMerge is true then the new edit is always merged.
+ */
+ private void recordEdit(EditOperation edit, boolean forceMerge) {
// Fetch the last edit operation and attempt to merge in the new edit.
final UndoManager um = mEditor.mUndoManager;
um.beginUpdate("Edit text");
@@ -4630,6 +4702,11 @@ public class Editor {
// Add this as the first edit.
if (DEBUG_UNDO) Log.d(TAG, "filter: adding first op " + edit);
um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
+ } else if (forceMerge) {
+ // Forced merges take priority because they could be the result of a non-user-edit
+ // change and this case should not create a new undo operation.
+ if (DEBUG_UNDO) Log.d(TAG, "filter: force merge " + edit);
+ lastEdit.forceMergeWith(edit);
} else if (!mIsUserEdit) {
// An application directly modified the Editable outside of a text edit. Treat this
// as a new change and don't attempt to merge.
@@ -4646,7 +4723,6 @@ public class Editor {
um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
}
um.endUpdate();
- return null; // Text not changed.
}
private boolean canUndoEdit(CharSequence source, int start, int end,
@@ -4678,6 +4754,23 @@ public class Editor {
return true;
}
+
+ private boolean isComposition(CharSequence source) {
+ if (!(source instanceof Spannable)) {
+ return false;
+ }
+ // This is a composition edit if the source has a non-zero-length composing span.
+ Spannable text = (Spannable) source;
+ int composeBegin = EditableInputConnection.getComposingSpanStart(text);
+ int composeEnd = EditableInputConnection.getComposingSpanEnd(text);
+ return composeBegin < composeEnd;
+ }
+
+ private boolean isInTextWatcher() {
+ CharSequence text = mEditor.mTextView.getText();
+ return (text instanceof SpannableStringBuilder)
+ && ((SpannableStringBuilder) text).getTextWatcherDepth() > 0;
+ }
}
/**
@@ -4689,7 +4782,6 @@ public class Editor {
private static final int TYPE_REPLACE = 2;
private int mType;
- private boolean mForceMerge;
private String mOldText;
private int mOldTextStart;
private String mNewText;
@@ -4699,17 +4791,13 @@ public class Editor {
private int mNewCursorPos;
/**
- * Constructs an edit operation from a text input operation that replaces the range
- * (dstart, dend) of dest with (start, end) of source. See {@link InputFilter#filter}.
- * If forceMerge is true then always forcibly merge this operation with any previous one.
+ * Constructs an edit operation from a text input operation on editor that replaces the
+ * oldText starting at dstart with newText.
*/
- public EditOperation(Editor editor, boolean forceMerge,
- CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+ public EditOperation(Editor editor, String oldText, int dstart, String newText) {
super(editor.mUndoOwner);
- mForceMerge = forceMerge;
-
- mOldText = dest.subSequence(dstart, dend).toString();
- mNewText = source.subSequence(start, end).toString();
+ mOldText = oldText;
+ mNewText = newText;
// Determine the type of the edit and store where it occurred. Avoid storing
// irrevelant data (e.g. mNewTextStart for a delete) because that makes the
@@ -4728,13 +4816,12 @@ public class Editor {
// Store cursor data.
mOldCursorPos = editor.mTextView.getSelectionStart();
- mNewCursorPos = dstart + (end - start);
+ mNewCursorPos = dstart + mNewText.length();
}
public EditOperation(Parcel src, ClassLoader loader) {
super(src, loader);
mType = src.readInt();
- mForceMerge = src.readInt() != 0;
mOldText = src.readString();
mOldTextStart = src.readInt();
mNewText = src.readString();
@@ -4746,7 +4833,6 @@ public class Editor {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
- dest.writeInt(mForceMerge ? 1 : 0);
dest.writeString(mOldText);
dest.writeInt(mOldTextStart);
dest.writeString(mNewText);
@@ -4798,10 +4884,6 @@ public class Editor {
Log.d(TAG, "mergeWith old " + this);
Log.d(TAG, "mergeWith new " + edit);
}
- if (edit.mForceMerge) {
- forceMergeWith(edit);
- return true;
- }
switch (mType) {
case TYPE_INSERT:
return mergeInsertWith(edit);
@@ -4859,7 +4941,7 @@ public class Editor {
* Forcibly creates a single merged edit operation by simulating the entire text
* contents being replaced.
*/
- private void forceMergeWith(EditOperation edit) {
+ public void forceMergeWith(EditOperation edit) {
if (DEBUG_UNDO) Log.d(TAG, "forceMerge");
Editor editor = getOwnerData();
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 133e102..21213ac 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -753,13 +753,13 @@ class FastScroller {
final View track = mTrackImage;
final View thumb = mThumbImage;
final Rect container = mContainerRect;
- final int containerWidth = container.width();
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(containerWidth, MeasureSpec.AT_MOST);
+ final int maxWidth = container.width();
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
track.measure(widthMeasureSpec, heightMeasureSpec);
final int trackWidth = track.getMeasuredWidth();
- final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
+ final int thumbHalfHeight = thumb.getHeight() / 2;
final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
final int right = left + trackWidth;
final int top = container.top + thumbHalfHeight;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f6d198b..0602944 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -53,8 +53,6 @@ import com.android.internal.R;
* only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
* is set to true.
*
- * @attr ref android.R.styleable#FrameLayout_foreground
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
* @attr ref android.R.styleable#FrameLayout_measureAllChildren
*/
@RemoteView
@@ -64,13 +62,6 @@ public class FrameLayout extends ViewGroup {
@ViewDebug.ExportedProperty(category = "measurement")
boolean mMeasureAllChildren = false;
- @ViewDebug.ExportedProperty(category = "drawing")
- private Drawable mForeground;
- private ColorStateList mForegroundTintList = null;
- private PorterDuff.Mode mForegroundTintMode = null;
- private boolean mHasForegroundTint = false;
- private boolean mHasForegroundTintMode = false;
-
@ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
@@ -85,15 +76,6 @@ public class FrameLayout extends ViewGroup {
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
-
- @ViewDebug.ExportedProperty(category = "drawing")
- private int mForegroundGravity = Gravity.FILL;
-
- /** {@hide} */
- @ViewDebug.ExportedProperty(category = "drawing")
- protected boolean mForegroundInPadding = true;
-
- boolean mForegroundBoundsChanged = false;
private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
@@ -115,48 +97,12 @@ public class FrameLayout extends ViewGroup {
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-
- mForegroundGravity = a.getInt(
- com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity);
-
- final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground);
- if (d != null) {
- setForeground(d);
- }
if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
setMeasureAllChildren(true);
}
- if (a.hasValue(R.styleable.FrameLayout_foregroundTintMode)) {
- mForegroundTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
- mHasForegroundTintMode = true;
- }
-
- if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
- mForegroundTintList = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
- mHasForegroundTint = true;
- }
-
- mForegroundInPadding = a.getBoolean(R.styleable.FrameLayout_foregroundInsidePadding, true);
-
a.recycle();
-
- applyForegroundTint();
- }
-
- /**
- * Describes how the foreground is positioned.
- *
- * @return foreground gravity.
- *
- * @see #setForegroundGravity(int)
- *
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
- */
- public int getForegroundGravity() {
- return mForegroundGravity;
}
/**
@@ -166,25 +112,18 @@ public class FrameLayout extends ViewGroup {
*
* @see #getForegroundGravity()
*
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
+ * @attr ref android.R.styleable#View_foregroundGravity
*/
@android.view.RemotableViewMethod
public void setForegroundGravity(int foregroundGravity) {
- if (mForegroundGravity != foregroundGravity) {
- if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.START;
- }
-
- if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.TOP;
- }
-
- mForegroundGravity = foregroundGravity;
-
+ if (getForegroundGravity() != foregroundGravity) {
+ super.setForegroundGravity(foregroundGravity);
- if (mForegroundGravity == Gravity.FILL && mForeground != null) {
+ // calling get* again here because the set above may apply default constraints
+ final Drawable foreground = getForeground();
+ if (getForegroundGravity() == Gravity.FILL && foreground != null) {
Rect padding = new Rect();
- if (mForeground.getPadding(padding)) {
+ if (foreground.getPadding(padding)) {
mForegroundPaddingLeft = padding.left;
mForegroundPaddingTop = padding.top;
mForegroundPaddingRight = padding.right;
@@ -201,53 +140,6 @@ public class FrameLayout extends ViewGroup {
}
}
- @Override
- protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
- super.onVisibilityChanged(changedView, visibility);
-
- final Drawable dr = mForeground;
- if (dr != null) {
- final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
- if (visible != dr.isVisible()) {
- dr.setVisible(visible, false);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || (who == mForeground);
- }
-
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mForeground != null) mForeground.jumpToCurrentState();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- if (mForeground != null && mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
-
- @Override
- public void drawableHotspotChanged(float x, float y) {
- super.drawableHotspotChanged(x, y);
-
- if (mForeground != null) {
- mForeground.setHotspot(x, y);
- }
- }
-
/**
* Returns a set of layout parameters with a width of
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
@@ -258,161 +150,23 @@ public class FrameLayout extends ViewGroup {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
- /**
- * Supply a Drawable that is to be rendered on top of all of the child
- * views in the frame layout. Any padding in the Drawable will be taken
- * into account by ensuring that the children are inset to be placed
- * inside of the padding area.
- *
- * @param d The Drawable to be drawn on top of the children.
- *
- * @attr ref android.R.styleable#FrameLayout_foreground
- */
- public void setForeground(Drawable d) {
- if (mForeground != d) {
- if (mForeground != null) {
- mForeground.setCallback(null);
- unscheduleDrawable(mForeground);
- }
-
- mForeground = d;
- mForegroundPaddingLeft = 0;
- mForegroundPaddingTop = 0;
- mForegroundPaddingRight = 0;
- mForegroundPaddingBottom = 0;
-
- if (d != null) {
- setWillNotDraw(false);
- d.setCallback(this);
- d.setLayoutDirection(getLayoutDirection());
- if (d.isStateful()) {
- d.setState(getDrawableState());
- }
- applyForegroundTint();
- if (mForegroundGravity == Gravity.FILL) {
- Rect padding = new Rect();
- if (d.getPadding(padding)) {
- mForegroundPaddingLeft = padding.left;
- mForegroundPaddingTop = padding.top;
- mForegroundPaddingRight = padding.right;
- mForegroundPaddingBottom = padding.bottom;
- }
- }
- } else {
- setWillNotDraw(true);
- }
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Returns the drawable used as the foreground of this FrameLayout. The
- * foreground drawable, if non-null, is always drawn on top of the children.
- *
- * @return A Drawable or null if no foreground was set.
- */
- public Drawable getForeground() {
- return mForeground;
- }
-
- /**
- * Applies a tint to the foreground drawable. Does not modify the current
- * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
- * <p>
- * Subsequent calls to {@link #setForeground(Drawable)} will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTintList(ColorStateList)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- *
- * @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #getForegroundTintList()
- * @see Drawable#setTintList(ColorStateList)
- */
- public void setForegroundTintList(@Nullable ColorStateList tint) {
- mForegroundTintList = tint;
- mHasForegroundTint = true;
-
- applyForegroundTint();
- }
-
- /**
- * @return the tint applied to the foreground drawable
- * @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #setForegroundTintList(ColorStateList)
- */
- @Nullable
- public ColorStateList getForegroundTintList() {
- return mForegroundTintList;
- }
-
- /**
- * Specifies the blending mode used to apply the tint specified by
- * {@link #setForegroundTintList(ColorStateList)}} to the foreground drawable.
- * The default mode is {@link PorterDuff.Mode#SRC_IN}.
- *
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #getForegroundTintMode()
- * @see Drawable#setTintMode(PorterDuff.Mode)
- */
- public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- mForegroundTintMode = tintMode;
- mHasForegroundTintMode = true;
-
- applyForegroundTint();
- }
-
- /**
- * @return the blending mode used to apply the tint to the foreground
- * drawable
- * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #setForegroundTintMode(PorterDuff.Mode)
- */
- @Nullable
- public PorterDuff.Mode getForegroundTintMode() {
- return mForegroundTintMode;
- }
-
- private void applyForegroundTint() {
- if (mForeground != null && (mHasForegroundTint || mHasForegroundTintMode)) {
- mForeground = mForeground.mutate();
-
- if (mHasForegroundTint) {
- mForeground.setTintList(mForegroundTintList);
- }
-
- if (mHasForegroundTintMode) {
- mForeground.setTintMode(mForegroundTintMode);
- }
-
- // The drawable (or one of its children) may not have been
- // stateful before applying the tint, so let's try again.
- if (mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
- }
-
int getPaddingLeftWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
mPaddingLeft + mForegroundPaddingLeft;
}
int getPaddingRightWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingRight, mForegroundPaddingRight) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
mPaddingRight + mForegroundPaddingRight;
}
private int getPaddingTopWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingTop, mForegroundPaddingTop) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
mPaddingTop + mForegroundPaddingTop;
}
private int getPaddingBottomWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
mPaddingBottom + mForegroundPaddingBottom;
}
@@ -527,8 +281,6 @@ public class FrameLayout extends ViewGroup {
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
- mForegroundBoundsChanged = true;
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
@@ -585,62 +337,6 @@ public class FrameLayout extends ViewGroup {
}
/**
- * {@inheritDoc}
- */
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mForegroundBoundsChanged = true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- if (mForeground != null) {
- final Drawable foreground = mForeground;
-
- if (mForegroundBoundsChanged) {
- mForegroundBoundsChanged = false;
- final Rect selfBounds = mSelfBounds;
- final Rect overlayBounds = mOverlayBounds;
-
- final int w = mRight-mLeft;
- final int h = mBottom-mTop;
-
- if (mForegroundInPadding) {
- selfBounds.set(0, 0, w, h);
- } else {
- selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
- }
-
- final int layoutDirection = getLayoutDirection();
- Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
- foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
- layoutDirection);
- foreground.setBounds(overlayBounds);
- }
-
- foreground.draw(canvas);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean gatherTransparentRegion(Region region) {
- boolean opaque = super.gatherTransparentRegion(region);
- if (region != null && mForeground != null) {
- applyDrawableToTransparentRegion(mForeground, region);
- }
- return opaque;
- }
-
- /**
* Sets whether to consider all children, or just those in
* the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
*
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index af5a8bf..b187c1c 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1209,13 +1209,13 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
- if (movePrevious()) {
+ if (moveDirection(-1)) {
playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (moveNext()) {
+ if (moveDirection(1)) {
playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
return true;
}
@@ -1255,18 +1255,12 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
return super.onKeyUp(keyCode, event);
}
- boolean movePrevious() {
- if (mItemCount > 0 && mSelectedPosition > 0) {
- scrollToChild(mSelectedPosition - mFirstPosition - 1);
- return true;
- } else {
- return false;
- }
- }
+ boolean moveDirection(int direction) {
+ direction = isLayoutRtl() ? -direction : direction;
+ int targetPosition = mSelectedPosition + direction;
- boolean moveNext() {
- if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
- scrollToChild(mSelectedPosition - mFirstPosition + 1);
+ if (mItemCount > 0 && targetPosition >= 0 && targetPosition < mItemCount) {
+ scrollToChild(targetPosition - mFirstPosition);
return true;
} else {
return false;
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index d85bbb9..310412f 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -567,13 +567,11 @@ public class ListPopupWindow {
public void show() {
int height = buildDropDown();
- int widthSpec = 0;
- int heightSpec = 0;
-
boolean noInputMethod = isInputMethodNotNeeded();
mPopup.setAllowScrollingAnchorParent(!noInputMethod);
if (mPopup.isShowing()) {
+ final int widthSpec;
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
// The call to PopupWindow's update method below can accept -1 for any
// value you do not want to update.
@@ -584,19 +582,19 @@ public class ListPopupWindow {
widthSpec = mDropDownWidth;
}
+ final int heightSpec;
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
// The call to PopupWindow's update method below can accept -1 for any
// value you do not want to update.
heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
if (noInputMethod) {
- mPopup.setWindowLayoutMode(
- mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
- ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+ mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0);
+ mPopup.setHeight(0);
} else {
- mPopup.setWindowLayoutMode(
- mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
- ViewGroup.LayoutParams.MATCH_PARENT : 0,
- ViewGroup.LayoutParams.MATCH_PARENT);
+ mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0);
+ mPopup.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
}
} else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
heightSpec = height;
@@ -604,32 +602,37 @@ public class ListPopupWindow {
heightSpec = mDropDownHeight;
}
+ mPopup.setWidth(widthSpec);
+ mPopup.setHeight(heightSpec);
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
- mDropDownVerticalOffset, widthSpec, heightSpec);
+ mDropDownVerticalOffset, -1, -1);
} else {
+ final int widthSpec;
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
- mPopup.setWidth(getAnchorView().getWidth());
+ widthSpec = getAnchorView().getWidth();
} else {
- mPopup.setWidth(mDropDownWidth);
+ widthSpec = mDropDownWidth;
}
}
+ final int heightSpec;
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
- mPopup.setHeight(height);
+ heightSpec = height;
} else {
- mPopup.setHeight(mDropDownHeight);
+ heightSpec = mDropDownHeight;
}
}
- mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+ mPopup.setWidth(widthSpec);
+ mPopup.setHeight(heightSpec);
mPopup.setClipToScreenEnabled(true);
// use outside touchable to dismiss drop down when touching outside of it, so
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f676254..8792323 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -46,6 +46,7 @@ import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import java.lang.ref.WeakReference;
@@ -126,10 +127,10 @@ public class PopupWindow {
private OnTouchListener mTouchInterceptor;
private int mWidthMode;
- private int mWidth;
+ private int mWidth = LayoutParams.WRAP_CONTENT;
private int mLastWidth;
private int mHeightMode;
- private int mHeight;
+ private int mHeight = LayoutParams.WRAP_CONTENT;
private int mLastHeight;
private int mPopupWidth;
@@ -907,17 +908,19 @@ public class PopupWindow {
* {@link ViewGroup.LayoutParams#WRAP_CONTENT},
* {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
* height.
+ *
+ * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
*/
+ @Deprecated
public void setWindowLayoutMode(int widthSpec, int heightSpec) {
mWidthMode = widthSpec;
mHeightMode = heightSpec;
}
/**
- * <p>Return this popup's height MeasureSpec</p>
+ * Returns the popup's height MeasureSpec.
*
* @return the height MeasureSpec of the popup
- *
* @see #setHeight(int)
*/
public int getHeight() {
@@ -925,13 +928,12 @@ public class PopupWindow {
}
/**
- * <p>Change the popup's height MeasureSpec</p>
- *
- * <p>If the popup is showing, calling this method will take effect only
- * the next time the popup is shown.</p>
+ * Sets the popup's height MeasureSpec.
+ * <p>
+ * If the popup is showing, calling this method will take effect the next
+ * time the popup is shown.
*
* @param height the height MeasureSpec of the popup
- *
* @see #getHeight()
* @see #isShowing()
*/
@@ -940,10 +942,9 @@ public class PopupWindow {
}
/**
- * <p>Return this popup's width MeasureSpec</p>
+ * Returns the popup's width MeasureSpec.
*
* @return the width MeasureSpec of the popup
- *
* @see #setWidth(int)
*/
public int getWidth() {
@@ -951,13 +952,12 @@ public class PopupWindow {
}
/**
- * <p>Change the popup's width MeasureSpec</p>
- *
- * <p>If the popup is showing, calling this method will take effect only
- * the next time the popup is shown.</p>
+ * Sets the popup's width MeasureSpec.
+ * <p>
+ * If the popup is showing, calling this method will take effect the next
+ * time the popup is shown.
*
* @param width the width MeasureSpec of the popup
- *
* @see #getWidth()
* @see #isShowing()
*/
@@ -1658,10 +1658,17 @@ public class PopupWindow {
/**
* Updates the state of the popup window, if it is currently being displayed,
- * from the currently set state. This includes:
- * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
- * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
- * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
+ * from the currently set state.
+ * <p>
+ * This includes:
+ * <ul>
+ * <li>{@link #setClippingEnabled(boolean)}</li>
+ * <li>{@link #setFocusable(boolean)}</li>
+ * <li>{@link #setIgnoreCheekPress()}</li>
+ * <li>{@link #setInputMethodMode(int)}</li>
+ * <li>{@link #setTouchable(boolean)}</li>
+ * <li>{@link #setAnimationStyle(int)}</li>
+ * </ul>
*/
public void update() {
if (!isShowing() || mContentView == null) {
@@ -1692,12 +1699,13 @@ public class PopupWindow {
}
/**
- * <p>Updates the dimension of the popup window. Calling this function
- * also updates the window with the current popup state as described
- * for {@link #update()}.</p>
+ * Updates the dimension of the popup window.
+ * <p>
+ * Calling this function also updates the window with the current popup
+ * state as described for {@link #update()}.
*
- * @param width the new width
- * @param height the new height
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(int width, int height) {
final WindowManager.LayoutParams p =
@@ -1706,40 +1714,43 @@ public class PopupWindow {
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
*
* @param x the new x location
* @param y the new y location
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(int x, int y, int width, int height) {
update(x, y, width, height, false);
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
*
* @param x the new x location
* @param y the new y location
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
- * @param force reposition the window even if the specified position
- * already seems to correspond to the LayoutParams
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
+ * @param force {@code true} to reposition the window even if the specified
+ * position already seems to correspond to the LayoutParams,
+ * {@code false} to only reposition if needed
*/
public void update(int x, int y, int width, int height, boolean force) {
- if (width != -1) {
+ if (width >= 0) {
mLastWidth = width;
setWidth(width);
}
- if (height != -1) {
+ if (height >= 0) {
mLastHeight = height;
setHeight(height);
}
@@ -1794,32 +1805,34 @@ public class PopupWindow {
}
/**
- * <p>Updates the position and the dimension of the popup window. Calling this
- * function also updates the window with the current popup state as described
- * for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Calling this function also updates the window with the current popup
+ * state as described for {@link #update()}.
*
* @param anchor the popup's anchor view
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int width, int height) {
update(anchor, false, 0, 0, true, width, height);
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
- *
- * <p>If the view later scrolls to move <code>anchor</code> to a different
- * location, the popup will be moved correspondingly.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
+ * <p>
+ * If the view later scrolls to move {@code anchor} to a different
+ * location, the popup will be moved correspondingly.
*
* @param anchor the popup's anchor view
* @param xoff x offset from the view's left edge
* @param yoff y offset from the view's bottom edge
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int xoff, int yoff, int width, int height) {
update(anchor, true, xoff, yoff, true, width, height);
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 28b4db2..20aa972 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -641,7 +641,7 @@ public class RadialTimePickerView extends View {
mCircleRadius = Math.min(mXCenter, mYCenter);
mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
- mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] - mSelectorRadius;
+ mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
calculatePositionsHours();
@@ -1144,30 +1144,31 @@ public class RadialTimePickerView extends View {
private void adjustPicker(int step) {
final int stepSize;
- final int initialValue;
+ final int initialStep;
final int maxValue;
final int minValue;
if (mShowHours) {
- stepSize = DEGREES_FOR_ONE_HOUR;
- initialValue = getCurrentHour() % 12;
+ stepSize = 1;
+ final int currentHour24 = getCurrentHour();
if (mIs24HourMode) {
- maxValue = 23;
+ initialStep = currentHour24;
minValue = 0;
+ maxValue = 23;
} else {
- maxValue = 12;
+ initialStep = hour24To12(currentHour24);
minValue = 1;
+ maxValue = 12;
}
} else {
- stepSize = DEGREES_FOR_ONE_MINUTE;
- initialValue = getCurrentMinute();
-
- maxValue = 55;
+ stepSize = 5;
+ initialStep = getCurrentMinute() / stepSize;
minValue = 0;
+ maxValue = 55;
}
- final int steppedValue = snapOnly30s(initialValue * stepSize, step) / stepSize;
- final int clampedValue = MathUtils.constrain(steppedValue, minValue, maxValue);
+ final int nextValue = (initialStep + step) * stepSize;
+ final int clampedValue = MathUtils.constrain(nextValue, minValue, maxValue);
if (mShowHours) {
setCurrentHour(clampedValue);
} else {
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
deleted file mode 100644
index c807d56..0000000
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2014 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.R;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SimpleMonthView.OnDayClickListener;
-
-import java.util.Calendar;
-
-/**
- * An adapter for a list of {@link android.widget.SimpleMonthView} items.
- */
-class SimpleMonthAdapter extends BaseAdapter {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
- private final Context mContext;
-
- private Calendar mSelectedDay = Calendar.getInstance();
- private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
- private ColorStateList mCalendarDayBackgroundColor = ColorStateList.valueOf(Color.MAGENTA);
- private OnDaySelectedListener mOnDaySelectedListener;
-
- private int mFirstDayOfWeek;
-
- public SimpleMonthAdapter(Context context) {
- mContext = context;
- }
-
- public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
- notifyDataSetInvalidated();
- }
-
- public void setFirstDayOfWeek(int firstDayOfWeek) {
- mFirstDayOfWeek = firstDayOfWeek;
-
- notifyDataSetInvalidated();
- }
-
- public int getFirstDayOfWeek() {
- return mFirstDayOfWeek;
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param day The day to highlight
- */
- public void setSelectedDay(Calendar day) {
- mSelectedDay = day;
-
- notifyDataSetChanged();
- }
-
- /**
- * Sets the listener to call when the user selects a day.
- *
- * @param listener The listener to call.
- */
- public void setOnDaySelectedListener(OnDaySelectedListener listener) {
- mOnDaySelectedListener = listener;
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mCalendarTextColors = colors;
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mCalendarDayBackgroundColor = dayBackgroundColor;
- }
-
- /**
- * Sets the text color, size, style, hint color, and highlight color from
- * the specified TextAppearance resource. This is mostly copied from
- * {@link TextView#setTextAppearance(Context, int)}.
- */
- void setCalendarTextAppearance(int resId) {
- final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
-
- final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
- if (textColor != null) {
- mCalendarTextColors = textColor;
- }
-
- // TODO: Support font size, etc.
-
- a.recycle();
- }
-
- @Override
- public int getCount() {
- final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
- final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
- return diffMonth + 12 * diffYear + 1;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final SimpleMonthView v;
- if (convertView != null) {
- v = (SimpleMonthView) convertView;
- } else {
- v = new SimpleMonthView(mContext);
-
- // Set up the new view
- final AbsListView.LayoutParams params = new AbsListView.LayoutParams(
- AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
- v.setLayoutParams(params);
- v.setClickable(true);
- v.setOnDayClickListener(mOnDayClickListener);
-
- v.setMonthTextColor(mCalendarTextColors);
- v.setDayOfWeekTextColor(mCalendarTextColors);
- v.setDayTextColor(mCalendarTextColors);
-
- v.setDayBackgroundColor(mCalendarDayBackgroundColor);
- }
-
- final int minMonth = mMinDate.get(Calendar.MONTH);
- final int minYear = mMinDate.get(Calendar.YEAR);
- final int currentMonth = position + minMonth;
- final int month = currentMonth % 12;
- final int year = currentMonth / 12 + minYear;
- final int selectedDay;
- if (isSelectedDayInMonth(year, month)) {
- selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
- } else {
- selectedDay = -1;
- }
-
- // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
- // height/number of weeks before being displayed.
- v.reuse();
-
- final int enabledDayRangeStart;
- if (minMonth == month && minYear == year) {
- enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeStart = 1;
- }
-
- final int enabledDayRangeEnd;
- if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
- enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeEnd = 31;
- }
-
- v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
- enabledDayRangeStart, enabledDayRangeEnd);
- v.invalidate();
-
- return v;
- }
-
- private boolean isSelectedDayInMonth(int year, int month) {
- return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
- }
-
- private boolean isCalendarInRange(Calendar value) {
- return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
- }
-
- private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
- @Override
- public void onDayClick(SimpleMonthView view, Calendar day) {
- if (day != null && isCalendarInRange(day)) {
- setSelectedDay(day);
-
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day);
- }
- }
- }
- };
-
- public interface OnDaySelectedListener {
- public void onDaySelected(SimpleMonthAdapter view, Calendar day);
- }
-}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 58ad515..4e5a39a 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,8 +18,8 @@ package android.widget;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
@@ -29,8 +29,6 @@ import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.StateSet;
@@ -38,13 +36,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.Formatter;
import java.util.Locale;
/**
@@ -52,93 +50,80 @@ import java.util.Locale;
* within the specified month.
*/
class SimpleMonthView extends View {
- private static final int MIN_ROW_HEIGHT = 10;
+ private static final int DAYS_IN_WEEK = 7;
+ private static final int MAX_WEEKS_IN_MONTH = 6;
private static final int DEFAULT_SELECTED_DAY = -1;
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
- private static final int DEFAULT_NUM_DAYS = 7;
- private static final int DEFAULT_NUM_ROWS = 6;
- private static final int MAX_NUM_ROWS = 6;
- private final Formatter mFormatter;
- private final StringBuilder mStringBuilder;
-
- private final int mMonthTextSize;
- private final int mDayOfWeekTextSize;
- private final int mDayTextSize;
-
- /** Height of the header containing the month and day of week labels. */
- private final int mMonthHeaderHeight;
+ private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+ private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
+ private final Paint mDaySelectorPaint = new Paint();
+ private final Paint mDayHighlightPaint = new Paint();
- private final Paint mDayBackgroundPaint = new Paint();
+ private final Calendar mCalendar = Calendar.getInstance();
+ private final Calendar mDayLabelCalendar = Calendar.getInstance();
- /** Single-letter (when available) formatter for the day of week label. */
- private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
+ private final MonthViewTouchHelper mTouchHelper;
- // affects the padding on the sides of this view
- private int mPadding = 0;
+ private final SimpleDateFormat mTitleFormatter;
+ private final SimpleDateFormat mDayOfWeekFormatter;
- private String mDayOfWeekTypeface;
- private String mMonthTypeface;
+ private CharSequence mTitle;
private int mMonth;
private int mYear;
- // Quick reference to the width of this view, matches parent
- private int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- private final int mRowHeight;
+ private int mPaddedWidth;
+ private int mPaddedHeight;
- // If this view contains the today
- private boolean mHasToday = false;
+ private final int mMonthHeight;
+ private final int mDayOfWeekHeight;
+ private final int mDayHeight;
+ private final int mCellWidth;
+ private final int mDaySelectorRadius;
- // Which day is selected [0-6] or -1 if no day is selected
+ /** The day of month for the selected day, or -1 if no day is selected. */
private int mActivatedDay = -1;
- // Which day is today [0-6] or -1 if no day is today
+ /**
+ * The day of month for today, or -1 if the today is not in the current
+ * month.
+ */
private int mToday = DEFAULT_SELECTED_DAY;
- // Which day of the week to start on [0-6]
+ /** The first day of the week (ex. Calendar.SUNDAY). */
private int mWeekStart = DEFAULT_WEEK_START;
- // How many days to display
- private int mNumDays = DEFAULT_NUM_DAYS;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells = mNumDays;
+ /** The number of days (ex. 28) in the current month. */
+ private int mDaysInMonth;
- private int mDayOfWeekStart = 0;
+ /**
+ * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+ * month.
+ */
+ private int mDayOfWeekStart;
- // First enabled day
+ /** The day of month for the first (inclusive) enabled day. */
private int mEnabledDayStart = 1;
- // Last enabled day
+ /** The day of month for the last (inclusive) enabled day. */
private int mEnabledDayEnd = 31;
- private final Calendar mCalendar = Calendar.getInstance();
- private final Calendar mDayLabelCalendar = Calendar.getInstance();
-
- private final MonthViewTouchHelper mTouchHelper;
-
- private int mNumRows = DEFAULT_NUM_ROWS;
+ /** 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
+ /** Optional listener for handling day click actions. */
private OnDayClickListener mOnDayClickListener;
- // Whether to prevent setting the accessibility delegate
- private boolean mLockAccessibilityDelegate;
-
- private int mNormalTextColor;
- private int mDisabledTextColor;
- private int mSelectedDayColor;
-
private ColorStateList mDayTextColor;
+ private int mTouchedDay = -1;
+
public SimpleMonthView(Context context) {
this(context, null);
}
@@ -155,64 +140,123 @@ class SimpleMonthView extends View {
super(context, attrs, defStyleAttr, defStyleRes);
final Resources res = context.getResources();
- mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
- mMonthTypeface = res.getString(R.string.sans_serif);
-
- mStringBuilder = new StringBuilder(50);
- mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
- mDayTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
- mMonthTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
- mDayOfWeekTextSize = res.getDimensionPixelSize(
- R.dimen.datepicker_month_day_label_text_size);
- mMonthHeaderHeight = res.getDimensionPixelOffset(
- R.dimen.datepicker_month_list_item_header_height);
-
- mRowHeight = Math.max(MIN_ROW_HEIGHT,
- (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
- - mMonthHeaderHeight) / MAX_NUM_ROWS);
+ mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+ mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+ mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+ mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+ mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
// Set up accessibility components.
mTouchHelper = new MonthViewTouchHelper(this);
setAccessibilityDelegate(mTouchHelper);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- mLockAccessibilityDelegate = true;
- initPaints();
+ final Locale locale = res.getConfiguration().locale;
+ final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
+ mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
+ mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
+
+ setClickable(true);
+ initPaints(res);
+ }
+
+ /**
+ * Applies the specified text appearance resource to a paint, returning the
+ * text color if one is set in the text appearance.
+ *
+ * @param p the paint to modify
+ * @param resId the resource ID of the text appearance
+ * @return the text color, if available
+ */
+ private ColorStateList applyTextAppearance(Paint p, int resId) {
+ final TypedArray ta = mContext.obtainStyledAttributes(null,
+ R.styleable.TextAppearance, 0, resId);
+
+ final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+ if (fontFamily != null) {
+ p.setTypeface(Typeface.create(fontFamily, 0));
+ }
+
+ p.setTextSize(ta.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
+
+ final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
+ p.setColor(enabledColor);
+ }
+
+ ta.recycle();
+
+ return textColor;
+ }
+
+ public void setMonthTextAppearance(int resId) {
+ applyTextAppearance(mMonthPaint, resId);
+ invalidate();
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ applyTextAppearance(mDayOfWeekPaint, resId);
+ invalidate();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
+ if (textColor != null) {
+ mDayTextColor = textColor;
+ }
+
+ invalidate();
+ }
+
+ public CharSequence getTitle() {
+ if (mTitle == null) {
+ mTitle = mTitleFormatter.format(mCalendar.getTime());
+ }
+ return mTitle;
}
/**
* Sets up the text and style properties for painting.
*/
- private void initPaints() {
+ private void initPaints(Resources res) {
+ final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
+ final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
+ final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
+
+ final int monthTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_month_text_size);
+ final int dayOfWeekTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_of_week_text_size);
+ final int dayTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_text_size);
+
mMonthPaint.setAntiAlias(true);
- mMonthPaint.setTextSize(mMonthTextSize);
- mMonthPaint.setTypeface(Typeface.create(mMonthTypeface, Typeface.BOLD));
+ mMonthPaint.setTextSize(monthTextSize);
+ mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
mMonthPaint.setTextAlign(Align.CENTER);
mMonthPaint.setStyle(Style.FILL);
mDayOfWeekPaint.setAntiAlias(true);
- mDayOfWeekPaint.setTextSize(mDayOfWeekTextSize);
- mDayOfWeekPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.BOLD));
+ mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
+ mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
mDayOfWeekPaint.setTextAlign(Align.CENTER);
mDayOfWeekPaint.setStyle(Style.FILL);
- mDayBackgroundPaint.setAntiAlias(true);
- mDayBackgroundPaint.setStyle(Style.FILL);
+ mDaySelectorPaint.setAntiAlias(true);
+ mDaySelectorPaint.setStyle(Style.FILL);
+
+ mDayHighlightPaint.setAntiAlias(true);
+ mDayHighlightPaint.setStyle(Style.FILL);
mDayPaint.setAntiAlias(true);
- mDayPaint.setTextSize(mDayTextSize);
+ mDayPaint.setTextSize(dayTextSize);
+ mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
mDayPaint.setTextAlign(Align.CENTER);
mDayPaint.setStyle(Style.FILL);
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
- }
-
void setMonthTextColor(ColorStateList monthTextColor) {
final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
mMonthPaint.setColor(enabledColor);
@@ -230,20 +274,18 @@ class SimpleMonthView extends View {
invalidate();
}
- void setDayBackgroundColor(ColorStateList dayBackgroundColor) {
+ void setDaySelectorColor(ColorStateList dayBackgroundColor) {
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- mDayBackgroundPaint.setColor(activatedColor);
+ mDaySelectorPaint.setColor(activatedColor);
invalidate();
}
- @Override
- public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- // Workaround for a JB MR1 issue where accessibility delegates on
- // top-level ListView items are overwritten.
- if (!mLockAccessibilityDelegate) {
- super.setAccessibilityDelegate(delegate);
- }
+ void setDayHighlightColor(ColorStateList dayHighlightColor) {
+ final int pressedColor = dayHighlightColor.getColorForState(
+ StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
+ mDayHighlightPaint.setColor(pressedColor);
+ invalidate();
}
public void setOnDayClickListener(OnDayClickListener listener) {
@@ -253,30 +295,124 @@ class SimpleMonthView extends View {
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// First right-of-refusal goes the touch exploration helper.
- if (mTouchHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
+ return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- final int day = getDayFromLocation(event.getX(), event.getY());
- if (day >= 0) {
- onDayClick(day);
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ final int touchedDay = getDayAtLocation(event.getX(), event.getY());
+ if (mTouchedDay != touchedDay) {
+ mTouchedDay = touchedDay;
+ invalidate();
}
break;
+
+ case MotionEvent.ACTION_UP:
+ final int clickedDay = getDayAtLocation(event.getX(), event.getY());
+ onDayClicked(clickedDay);
+ // Fall through.
+ case MotionEvent.ACTION_CANCEL:
+ // Reset touched day on stream end.
+ mTouchedDay = -1;
+ invalidate();
+ break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
- drawMonthTitle(canvas);
- drawWeekDayLabels(canvas);
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ canvas.translate(paddingLeft, paddingTop);
+
+ drawMonth(canvas);
+ drawDaysOfWeek(canvas);
drawDays(canvas);
+
+ canvas.translate(-paddingLeft, -paddingTop);
+ }
+
+ private void drawMonth(Canvas canvas) {
+ final float x = mPaddedWidth / 2f;
+
+ // Vertically centered within the month header height.
+ final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
+ final float y = (mMonthHeight - lineHeight) / 2f;
+
+ canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+ }
+
+ private void drawDaysOfWeek(Canvas canvas) {
+ final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
+ final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
+ mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+
+ final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
+ final float x = (2 * i + 1) * cellWidthHalf;
+ canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ }
+ }
+
+ /**
+ * Draws the month days.
+ */
+ private void drawDays(Canvas canvas) {
+ final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
+ float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+
+ for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
+ final int x = (2 * j + 1) * cellWidthHalf;
+ int stateMask = 0;
+
+ if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+ stateMask |= StateSet.VIEW_STATE_ENABLED;
+ }
+
+ final boolean isDayActivated = mActivatedDay == day;
+ if (isDayActivated) {
+ stateMask |= StateSet.VIEW_STATE_ACTIVATED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
+ } else if (mTouchedDay == day) {
+ stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+ }
+
+ final boolean isDayToday = mToday == day;
+ final int dayTextColor;
+ if (isDayToday && !isDayActivated) {
+ dayTextColor = mDaySelectorPaint.getColor();
+ } else {
+ final int[] stateSet = StateSet.get(stateMask);
+ dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
+ }
+ mDayPaint.setColor(dayTextColor);
+
+ canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+
+ j++;
+
+ if (j == DAYS_IN_WEEK) {
+ j = 0;
+ centerY += mDayHeight;
+ }
+ }
}
private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +424,52 @@ class SimpleMonthView extends View {
}
/**
- * Sets all the parameters for displaying this week. Parameters have a default value and
- * will only update if a new value is included, except for focus month, which will always
- * default to no focus month if no value is passed in. The only required parameter is the
- * week start.
+ * Sets the selected day.
*
- * @param selectedDay the selected day of the month, or -1 for no selection.
- * @param month the month.
- * @param year the year.
- * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
- * {@link Calendar#SATURDAY}.
- * @param enabledDayStart the first enabled day.
- * @param enabledDayEnd the last enabled day.
+ * @param dayOfMonth the selected day of the month, or {@code -1} to clear
+ * the selection
+ */
+ public void setSelectedDay(int dayOfMonth) {
+ mActivatedDay = dayOfMonth;
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ if (isValidDayOfWeek(weekStart)) {
+ mWeekStart = weekStart;
+ } else {
+ mWeekStart = mCalendar.getFirstDayOfWeek();
+ }
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets all the parameters for displaying this week.
+ * <p>
+ * Parameters have a default value and will only update if a new value is
+ * included, except for focus month, which will always default to no focus
+ * month if no value is passed in. The only required parameter is the week
+ * start.
+ *
+ * @param selectedDay the selected day of the month, or -1 for no selection
+ * @param month the month
+ * @param year the year
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ * @param enabledDayStart the first enabled day
+ * @param enabledDayEnd the last enabled day
*/
void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
int enabledDayEnd) {
@@ -310,12 +480,6 @@ class SimpleMonthView extends View {
}
mYear = year;
- // Figure out what day today is
- final Time today = new Time(Time.getCurrentTimezone());
- today.setToNow();
- mHasToday = false;
- mToday = -1;
-
mCalendar.set(Calendar.MONTH, mMonth);
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
@@ -334,15 +498,20 @@ class SimpleMonthView extends View {
mEnabledDayEnd = enabledDayEnd;
}
- mNumCells = getDaysInMonth(mMonth, mYear);
- for (int i = 0; i < mNumCells; i++) {
+ // Figure out what day today is.
+ final Calendar today = Calendar.getInstance();
+ mToday = -1;
+ mDaysInMonth = getDaysInMonth(mMonth, mYear);
+ for (int i = 0; i < mDaysInMonth; i++) {
final int day = i + 1;
if (sameDay(day, today)) {
- mHasToday = true;
mToday = day;
}
}
- mNumRows = calculateNumRows();
+ mNumWeeks = calculateNumRows();
+
+ // Invalidate the old title.
+ mTitle = null;
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
@@ -371,154 +540,118 @@ class SimpleMonthView extends View {
}
public void reuse() {
- mNumRows = DEFAULT_NUM_ROWS;
+ mNumWeeks = MAX_WEEKS_IN_MONTH;
requestLayout();
}
private int calculateNumRows() {
- int offset = findDayOffset();
- int dividend = (offset + mNumCells) / mNumDays;
- int remainder = (offset + mNumCells) % mNumDays;
- return (dividend + (remainder > 0 ? 1 : 0));
+ final int offset = findDayOffset();
+ final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
+ return dividend + (remainder > 0 ? 1 : 0);
}
- private boolean sameDay(int day, Time today) {
- return mYear == today.year &&
- mMonth == today.month &&
- day == today.monthDay;
+ private boolean sameDay(int day, Calendar today) {
+ return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
+ && day == today.get(Calendar.DAY_OF_MONTH);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
- + mMonthHeaderHeight);
+ final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight
+ + getPaddingTop() + getPaddingBottom();
+ final int preferredWidth = mCellWidth * DAYS_IN_WEEK
+ + getPaddingStart() + getPaddingEnd();
+ final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
+ final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
+ setMeasuredDimension(resolvedWidth, resolvedHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
+ mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
+ mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
}
- private String getMonthAndYearString() {
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
- | DateUtils.FORMAT_NO_MONTH_DAY;
- mStringBuilder.setLength(0);
- long millis = mCalendar.getTimeInMillis();
- return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
- Time.getCurrentTimezone()).toString();
- }
-
- private void drawMonthTitle(Canvas canvas) {
- final float x = (mWidth + 2 * mPadding) / 2f;
-
- // Centered on the upper half of the month header.
- final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
- final float y = mMonthHeaderHeight * 0.25f - lineHeight / 2f;
-
- canvas.drawText(getMonthAndYearString(), x, y, mMonthPaint);
- }
-
- private void drawWeekDayLabels(Canvas canvas) {
- final float dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered on the lower half of the month header.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- final float y = mMonthHeaderHeight * 0.75f - lineHeight / 2f;
-
- for (int i = 0; i < mNumDays; i++) {
- final int calendarDay = (i + mWeekStart) % mNumDays;
- mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
- final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
- final float x = (2 * i + 1) * dayWidthHalf + mPadding;
- canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ private int findDayOffset() {
+ final int offset = mDayOfWeekStart - mWeekStart;
+ if (mDayOfWeekStart < mWeekStart) {
+ return offset + DAYS_IN_WEEK;
}
+ return offset;
}
/**
- * Draws the month days.
+ * 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) or -1 if the position wasn't in a
+ * valid day
*/
- private void drawDays(Canvas canvas) {
- final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered within the row.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- float y = mMonthHeaderHeight + (mRowHeight - lineHeight) / 2f;
-
- for (int day = 1, j = findDayOffset(); day <= mNumCells; day++) {
- final int x = (2 * j + 1) * dayWidthHalf + mPadding;
- int stateMask = 0;
-
- if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
- stateMask |= StateSet.VIEW_STATE_ENABLED;
- }
-
- if (mActivatedDay == day) {
- stateMask |= StateSet.VIEW_STATE_ACTIVATED;
-
- // Adjust the circle to be centered the row.
- final float rowCenterY = y + lineHeight / 2;
- canvas.drawCircle(x, rowCenterY, mRowHeight / 2,
- mDayBackgroundPaint);
- }
-
- final int[] stateSet = StateSet.get(stateMask);
- final int dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
- mDayPaint.setColor(dayTextColor);
-
- final boolean isDayToday = mHasToday && mToday == day;
- mDayPaint.setFakeBoldText(isDayToday);
-
- canvas.drawText(String.format("%d", day), x, y, mDayPaint);
+ private int getDayAtLocation(float x, float y) {
+ final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+ if (paddedX < 0 || paddedX >= mPaddedWidth) {
+ return -1;
+ }
- j++;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+ if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
+ return -1;
+ }
- if (j == mNumDays) {
- j = 0;
- y += mRowHeight;
- }
+ final int row = (paddedY - headerHeight) / mDayHeight;
+ final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int index = col + row * DAYS_IN_WEEK;
+ final int day = index + 1 - findDayOffset();
+ if (day < 1 || day > mDaysInMonth) {
+ return -1;
}
- }
- private int findDayOffset() {
- return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
- - mWeekStart;
+ return day;
}
/**
- * Calculates the day that the given x position is in, accounting for week
- * number. Returns the day or -1 if the position wasn't in a day.
+ * Calculates the bounds of the specified day.
*
- * @param x The x position of the touch event
- * @return The day number, or -1 if the position wasn't in a day
+ * @param day the day of the month
+ * @param outBounds the rect to populate with bounds
*/
- private int getDayFromLocation(float x, float y) {
- int dayStart = mPadding;
- if (x < dayStart || x > mWidth - mPadding) {
- return -1;
+ private boolean getBoundsForDay(int day, Rect outBounds) {
+ if (day < 1 || day > mDaysInMonth) {
+ return false;
}
- // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
- int row = (int) (y - mMonthHeaderHeight) / mRowHeight;
- int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
- int day = column - findDayOffset() + 1;
- day += row * mNumDays;
- if (day < 1 || day > mNumCells) {
- return -1;
- }
- return day;
+ final int index = day - 1 + findDayOffset();
+ final int row = index / DAYS_IN_WEEK;
+ final int col = index % DAYS_IN_WEEK;
+
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = row * mDayHeight + headerHeight;
+ final int paddedX = col * mPaddedWidth;
+
+ final int y = paddedY + getPaddingTop();
+ final int x = paddedX + getPaddingLeft();
+
+ final int cellHeight = mDayHeight;
+ final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+ outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
+
+ return true;
}
/**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
- * @param day The day that was clicked
+ * @param day the day that was clicked
*/
- private void onDayClick(int day) {
+ private void onDayClicked(int day) {
if (mOnDayClickListener != null) {
Calendar date = Calendar.getInstance();
date.set(mYear, mMonth, day);
@@ -530,44 +663,6 @@ class SimpleMonthView extends View {
}
/**
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus
- */
- Calendar getAccessibilityFocus() {
- final int day = mTouchHelper.getFocusedVirtualView();
- Calendar date = null;
- if (day >= 0) {
- date = Calendar.getInstance();
- date.set(mYear, mMonth, day);
- }
- return date;
- }
-
- /**
- * Clears accessibility focus within the view. No-op if the view does not
- * contain accessibility focus.
- */
- public void clearAccessibilityFocus() {
- mTouchHelper.clearFocusedVirtualView();
- }
-
- /**
- * Attempts to restore accessibility focus to the specified date.
- *
- * @param day The date which should receive focus
- * @return {@code false} if the date is not valid for this month view, or
- * {@code true} if the date received focus
- */
- boolean restoreAccessibilityFocus(Calendar day) {
- if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
- (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
- return false;
- }
- mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
- return true;
- }
-
- /**
* Provides a virtual view hierarchy for interfacing with an accessibility
* service.
*/
@@ -581,24 +676,9 @@ class SimpleMonthView extends View {
super(host);
}
- public void setFocusedVirtualView(int virtualViewId) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
-
- public void clearFocusedVirtualView() {
- final int focusedVirtualView = getFocusedVirtualView();
- if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- focusedVirtualView,
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
- null);
- }
- }
-
@Override
protected int getVirtualViewAt(float x, float y) {
- final int day = getDayFromLocation(x, y);
+ final int day = getDayAtLocation(x, y);
if (day >= 0) {
return day;
}
@@ -607,7 +687,7 @@ class SimpleMonthView extends View {
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
- for (int day = 1; day <= mNumCells; day++) {
+ for (int day = 1; day <= mDaysInMonth; day++) {
virtualViewIds.add(day);
}
}
@@ -619,11 +699,20 @@ class SimpleMonthView extends View {
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
- getItemBounds(virtualViewId, mTempRect);
+ final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+
+ if (!hasBounds) {
+ // The day is invalid, kill the node.
+ mTempRect.setEmpty();
+ node.setContentDescription("");
+ node.setBoundsInParent(mTempRect);
+ node.setVisibleToUser(false);
+ return;
+ }
node.setContentDescription(getItemDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
- node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ node.addAction(AccessibilityAction.ACTION_CLICK);
if (virtualViewId == mActivatedDay) {
node.setSelected(true);
@@ -636,7 +725,7 @@ class SimpleMonthView extends View {
Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- onDayClick(virtualViewId);
+ onDayClicked(virtualViewId);
return true;
}
@@ -644,26 +733,6 @@ class SimpleMonthView extends View {
}
/**
- * Calculates the bounding rectangle of a given time object.
- *
- * @param day The day to calculate bounds for
- * @param rect The rectangle in which to store the bounds
- */
- private void getItemBounds(int day, Rect rect) {
- final int offsetX = mPadding;
- final int offsetY = mMonthHeaderHeight;
- final int cellHeight = mRowHeight;
- final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
- final int index = ((day - 1) + findDayOffset());
- final int row = (index / mNumDays);
- final int column = (index % mNumDays);
- final int x = (offsetX + (column * cellWidth));
- final int y = (offsetY + (row * cellHeight));
-
- rect.set(x, y, (x + cellWidth), (y + cellHeight));
- }
-
- /**
* Generates a description for a given time object. Since this
* description will be spoken, the components are ordered by descending
* specificity as DAY MONTH YEAR.
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index 4323851..aad0625 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -378,7 +378,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene
// Lazily get the URL color from the current theme.
TypedValue colorValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
- mUrlColor = context.getResources().getColorStateList(colorValue.resourceId);
+ mUrlColor = context.getColorStateList(colorValue.resourceId);
}
SpannableString text = new SpannableString(url);
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 110d79b..c521f72 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -603,7 +603,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
// Donut apps get old color scheme
tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
- tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
+ tv.setTextColor(context.getColorStateList(R.color.tab_indicator_text_v4));
}
return tabIndicator;
@@ -648,7 +648,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
// Donut apps get old color scheme
tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
- tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
+ tv.setTextColor(context.getColorStateList(R.color.tab_indicator_text_v4));
}
return tabIndicator;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 447e9ac..718ef93 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -111,8 +111,6 @@ import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAssistStructure;
@@ -2644,94 +2642,92 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Sets the text appearance from the specified style resource.
+ * <p>
+ * Use a framework-defined {@code TextAppearance} style like
+ * {@link android.R.style#TextAppearance_Material_Body1 @android:style/TextAppearance.Material.Body1}
+ * or see {@link android.R.styleable#TextAppearance TextAppearance} for the
+ * set of attributes that can be used in a custom style.
+ *
+ * @param resId the resource identifier of the style to apply
+ * @attr ref android.R.styleable#TextView_textAppearance
+ */
+ @SuppressWarnings("deprecation")
+ public void setTextAppearance(@StyleRes int resId) {
+ setTextAppearance(mContext, resId);
+ }
+
+ /**
* Sets the text color, size, style, hint color, and highlight color
* from the specified TextAppearance resource.
+ *
+ * @deprecated Use {@link #setTextAppearance(int)} instead.
*/
- public void setTextAppearance(Context context, @StyleRes int resid) {
- TypedArray appearance =
- context.obtainStyledAttributes(resid,
- com.android.internal.R.styleable.TextAppearance);
-
- int color;
- ColorStateList colors;
- int ts;
+ @Deprecated
+ public void setTextAppearance(Context context, @StyleRes int resId) {
+ final TypedArray ta = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
- color = appearance.getColor(
- com.android.internal.R.styleable.TextAppearance_textColorHighlight, 0);
- if (color != 0) {
- setHighlightColor(color);
+ final int textColorHighlight = ta.getColor(
+ R.styleable.TextAppearance_textColorHighlight, 0);
+ if (textColorHighlight != 0) {
+ setHighlightColor(textColorHighlight);
}
- colors = appearance.getColorStateList(com.android.internal.R.styleable.
- TextAppearance_textColor);
- if (colors != null) {
- setTextColor(colors);
+ final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ setTextColor(textColor);
}
- ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
- TextAppearance_textSize, 0);
- if (ts != 0) {
- setRawTextSize(ts);
+ final int textSize = ta.getDimensionPixelSize(R.styleable.TextAppearance_textSize, 0);
+ if (textSize != 0) {
+ setRawTextSize(textSize);
}
- colors = appearance.getColorStateList(com.android.internal.R.styleable.
- TextAppearance_textColorHint);
- if (colors != null) {
- setHintTextColor(colors);
+ final ColorStateList textColorHint = ta.getColorStateList(
+ R.styleable.TextAppearance_textColorHint);
+ if (textColorHint != null) {
+ setHintTextColor(textColorHint);
}
- colors = appearance.getColorStateList(com.android.internal.R.styleable.
- TextAppearance_textColorLink);
- if (colors != null) {
- setLinkTextColor(colors);
+ final ColorStateList textColorLink = ta.getColorStateList(
+ R.styleable.TextAppearance_textColorLink);
+ if (textColorLink != null) {
+ setLinkTextColor(textColorLink);
}
- String familyName;
- int typefaceIndex, styleIndex;
-
- familyName = appearance.getString(com.android.internal.R.styleable.
- TextAppearance_fontFamily);
- typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
- TextAppearance_typeface, -1);
- styleIndex = appearance.getInt(com.android.internal.R.styleable.
- TextAppearance_textStyle, -1);
-
- setTypefaceFromAttrs(familyName, typefaceIndex, styleIndex);
-
- final int shadowcolor = appearance.getInt(
- com.android.internal.R.styleable.TextAppearance_shadowColor, 0);
- if (shadowcolor != 0) {
- final float dx = appearance.getFloat(
- com.android.internal.R.styleable.TextAppearance_shadowDx, 0);
- final float dy = appearance.getFloat(
- com.android.internal.R.styleable.TextAppearance_shadowDy, 0);
- final float r = appearance.getFloat(
- com.android.internal.R.styleable.TextAppearance_shadowRadius, 0);
+ final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+ final int typefaceIndex = ta.getInt(R.styleable.TextAppearance_typeface, -1);
+ final int styleIndex = ta.getInt(R.styleable.TextAppearance_textStyle, -1);
+ setTypefaceFromAttrs(fontFamily, typefaceIndex, styleIndex);
- setShadowLayer(r, dx, dy, shadowcolor);
+ final int shadowColor = ta.getInt(R.styleable.TextAppearance_shadowColor, 0);
+ if (shadowColor != 0) {
+ final float dx = ta.getFloat(R.styleable.TextAppearance_shadowDx, 0);
+ final float dy = ta.getFloat(R.styleable.TextAppearance_shadowDy, 0);
+ final float r = ta.getFloat(R.styleable.TextAppearance_shadowRadius, 0);
+ setShadowLayer(r, dx, dy, shadowColor);
}
- if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps,
- false)) {
+ if (ta.getBoolean(R.styleable.TextAppearance_textAllCaps, false)) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
}
- if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_elegantTextHeight)) {
- setElegantTextHeight(appearance.getBoolean(
- com.android.internal.R.styleable.TextAppearance_elegantTextHeight, false));
+ if (ta.hasValue(R.styleable.TextAppearance_elegantTextHeight)) {
+ setElegantTextHeight(ta.getBoolean(
+ R.styleable.TextAppearance_elegantTextHeight, false));
}
- if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_letterSpacing)) {
- setLetterSpacing(appearance.getFloat(
- com.android.internal.R.styleable.TextAppearance_letterSpacing, 0));
+ if (ta.hasValue(R.styleable.TextAppearance_letterSpacing)) {
+ setLetterSpacing(ta.getFloat(
+ R.styleable.TextAppearance_letterSpacing, 0));
}
- if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_fontFeatureSettings)) {
- setFontFeatureSettings(appearance.getString(
- com.android.internal.R.styleable.TextAppearance_fontFeatureSettings));
+ if (ta.hasValue(R.styleable.TextAppearance_fontFeatureSettings)) {
+ setFontFeatureSettings(ta.getString(
+ R.styleable.TextAppearance_fontFeatureSettings));
}
- appearance.recycle();
+ ta.recycle();
}
/**
@@ -5106,7 +5102,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// - onFocusChanged cannot start it when focus is given to a view with selected text (after
// a screen rotation) since layout is not yet initialized at that point.
if (mEditor != null && mEditor.mCreatedWithASelection) {
- mEditor.startSelectionActionMode();
+ mEditor.startSelectionActionModeWithSelection();
mEditor.mCreatedWithASelection = false;
}
@@ -5114,7 +5110,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
// not be set. Do the test here instead.
if (this instanceof ExtractEditText && hasSelection() && mEditor != null) {
- mEditor.startSelectionActionMode();
+ mEditor.startSelectionActionModeWithSelection();
}
unregisterForPreDraw();
@@ -8747,7 +8743,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Selection.setSelection((Spannable) text, start, end);
// Make sure selection mode is engaged.
if (mEditor != null) {
- mEditor.startSelectionActionMode();
+ mEditor.startSelectionActionModeWithSelection();
}
return true;
}
@@ -8959,9 +8955,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* A custom implementation can add new entries in the default menu in its
* {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
- * default actions can also be removed from the menu using {@link Menu#removeItem(int)} and
- * passing {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy}
- * or {@link android.R.id#paste} ids as parameters.
+ * default actions can also be removed from the menu using
+ * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
+ * {@link android.R.id#cut}, {@link android.R.id#copy} or {@link android.R.id#paste} ids as
+ * parameters.
*
* Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
@@ -9536,7 +9533,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// TODO: Add an option to configure this
private static final float MARQUEE_DELTA_MAX = 0.07f;
private static final int MARQUEE_DELAY = 1200;
- private static final int MARQUEE_RESTART_DELAY = 1200;
private static final int MARQUEE_DP_PER_SECOND = 30;
private static final byte MARQUEE_STOPPED = 0x0;
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
deleted file mode 100644
index d3c786c..0000000
--- a/core/java/android/widget/TextViewWithCircularIndicator.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-
-import com.android.internal.R;
-
-class TextViewWithCircularIndicator extends TextView {
- private final Paint mCirclePaint = new Paint();
- private final String mItemIsSelectedText;
-
- public TextViewWithCircularIndicator(Context context) {
- this(context, null);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- final Resources res = context.getResources();
- mItemIsSelectedText = res.getString(R.string.item_is_selected);
-
- init();
- }
-
- private void init() {
- mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
- mCirclePaint.setAntiAlias(true);
- mCirclePaint.setTextAlign(Paint.Align.CENTER);
- mCirclePaint.setStyle(Paint.Style.FILL);
- }
-
- public void setCircleColor(int color) {
- mCirclePaint.setColor(color);
- invalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- if (isActivated()) {
- final int width = getWidth();
- final int height = getHeight();
- final int radius = Math.min(width, height) / 2;
- canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
- }
-
- super.onDraw(canvas);
- }
-
- @Override
- public CharSequence getContentDescription() {
- final CharSequence itemText = getText();
- if (isActivated()) {
- return String.format(mItemIsSelectedText, itemText);
- } else {
- return itemText;
- }
- }
-} \ No newline at end of file
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 6f0465f..7bd502e 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -17,10 +17,9 @@
package android.widget;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.StateSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -32,23 +31,14 @@ import com.android.internal.R;
/**
* Displays a selectable list of years.
*/
-class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
- OnDateChangedListener {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
+class YearPickerView extends ListView {
private final YearAdapter mAdapter;
private final int mViewSize;
private final int mChildSize;
- private DatePickerController mController;
-
- private int mSelectedPosition = -1;
- private int mYearActivatedColor;
+ private OnYearSelectedListener mOnYearSelectedListener;
- public YearPickerView(Context context) {
- this(context, null);
- }
+ private long mCurrentTimeMillis;
public YearPickerView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listViewStyle);
@@ -69,104 +59,187 @@ class YearPickerView extends ListView implements AdapterView.OnItemClickListener
mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
- setVerticalFadingEdgeEnabled(true);
- setFadingEdgeLength(mChildSize / 3);
-
- final int paddingTop = res.getDimensionPixelSize(
- R.dimen.datepicker_year_picker_padding_top);
- setPadding(0, paddingTop, 0, 0);
+ setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final int year = mAdapter.getYearForPosition(position);
+ mAdapter.setSelection(year);
- setOnItemClickListener(this);
- setDividerHeight(0);
+ if (mOnYearSelectedListener != null) {
+ mOnYearSelectedListener.onYearChanged(YearPickerView.this, year);
+ }
+ }
+ });
- mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+ mAdapter = new YearAdapter(getContext());
setAdapter(mAdapter);
}
- public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
+ public void setOnYearSelectedListener(OnYearSelectedListener listener) {
+ mOnYearSelectedListener = listener;
+ }
- updateAdapterData();
+ public void setDate(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
}
- public void init(DatePickerController controller) {
- mController = controller;
- mController.registerOnDateChangedListener(this);
+ /**
+ * Sets the currently selected year. Jumps immediately to the new year.
+ *
+ * @param year the target year
+ */
+ public void setYear(final int year) {
+ mAdapter.setSelection(year);
- updateAdapterData();
+ post(new Runnable() {
+ @Override
+ public void run() {
+ final int position = mAdapter.getPositionForYear(year);
+ if (position >= 0 && position < getCount()) {
+ setSelectionCentered(position);
+ }
+ }
+ });
+ }
- onDateChanged();
+ public void setSelectionCentered(int position) {
+ final int offset = mViewSize / 2 - mChildSize / 2;
+ setSelectionFromTop(position, offset);
}
- public void setYearBackgroundColor(ColorStateList yearBackgroundColor) {
- mYearActivatedColor = yearBackgroundColor.getColorForState(
- StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- invalidate();
+ public void setRange(Calendar min, Calendar max) {
+ mAdapter.setRange(min, max);
}
public void setYearTextAppearance(int resId) {
mAdapter.setItemTextAppearance(resId);
}
- private void updateAdapterData() {
- mAdapter.clear();
+ public void setYearActivatedTextAppearance(int resId) {
+ mAdapter.setItemActivatedTextAppearance(resId);
+ }
+
+ private static class YearAdapter extends BaseAdapter {
+ private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
+
+ private final LayoutInflater mInflater;
+
+ private int mActivatedYear;
+ private int mMinYear;
+ private int mCount;
- final int maxYear = mMaxDate.get(Calendar.YEAR);
- for (int year = mMinDate.get(Calendar.YEAR); year <= maxYear; year++) {
- mAdapter.add(year);
+ private int mItemTextAppearanceResId;
+ private int mItemActivatedTextAppearanceResId;
+
+ public YearAdapter(Context context) {
+ mInflater = LayoutInflater.from(context);
}
- }
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mController.tryVibrate();
- if (position != mSelectedPosition) {
- mSelectedPosition = position;
- mAdapter.notifyDataSetChanged();
+ public void setRange(Calendar minDate, Calendar maxDate) {
+ final int minYear = minDate.get(Calendar.YEAR);
+ final int count = maxDate.get(Calendar.YEAR) - minYear + 1;
+
+ if (mMinYear != minYear || mCount != count) {
+ mMinYear = minYear;
+ mCount = count;
+ notifyDataSetInvalidated();
+ }
}
- mController.onYearSelected(mAdapter.getItem(position));
- }
- private class YearAdapter extends ArrayAdapter<Integer> {
- private int mItemTextAppearanceResId;
+ public boolean setSelection(int year) {
+ if (mActivatedYear != year) {
+ mActivatedYear = year;
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public void setItemTextAppearance(int resId) {
+ mItemTextAppearanceResId = resId;
+ notifyDataSetChanged();
+ }
+
+ public void setItemActivatedTextAppearance(int resId) {
+ mItemActivatedTextAppearanceResId = resId;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
- public YearAdapter(Context context, int resource) {
- super(context, resource);
+ @Override
+ public Integer getItem(int position) {
+ return getYearForPosition(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getYearForPosition(position);
+ }
+
+ public int getPositionForYear(int year) {
+ return year - mMinYear;
+ }
+
+ public int getYearForPosition(int position) {
+ return mMinYear + position;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- final TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
- super.getView(position, convertView, parent);
- v.setTextAppearance(v.getContext(), mItemTextAppearanceResId);
- v.setCircleColor(mYearActivatedColor);
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
- final int year = getItem(position);
- final boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
- v.setActivated(selected);
+ final int year = getYearForPosition(position);
+ final boolean activated = mActivatedYear == year;
+ final int textAppearanceResId;
+ if (activated && mItemActivatedTextAppearanceResId != 0) {
+ textAppearanceResId = mItemActivatedTextAppearanceResId;
+ } else {
+ textAppearanceResId = mItemTextAppearanceResId;
+ }
+
+ final TextView v = (TextView) convertView;
+ v.setText("" + year);
+ v.setTextAppearance(v.getContext(), textAppearanceResId);
+ v.setActivated(activated);
return v;
}
- public void setItemTextAppearance(int resId) {
- mItemTextAppearanceResId = resId;
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
}
- }
- public void postSetSelectionCentered(final int position) {
- postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
- }
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
- public void postSetSelectionFromTop(final int position, final int offset) {
- post(new Runnable() {
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
- @Override
- public void run() {
- setSelectionFromTop(position, offset);
- requestLayout();
- }
- });
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
}
public int getFirstPositionOffset() {
@@ -177,22 +250,28 @@ class YearPickerView extends ListView implements AdapterView.OnItemClickListener
return firstChild.getTop();
}
- @Override
- public void onDateChanged() {
- updateAdapterData();
- mAdapter.notifyDataSetChanged();
- postSetSelectionCentered(
- mController.getSelectedDay().get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
+ // There are a bunch of years, so don't bother.
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setFromIndex(0);
event.setToIndex(0);
}
}
+
+ /**
+ * The callback used to indicate the user changed the year.
+ */
+ public interface OnYearSelectedListener {
+ /**
+ * Called upon a year change.
+ *
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ */
+ void onYearChanged(YearPickerView view, int year);
+ }
} \ No newline at end of file
diff --git a/core/java/android/hardware/IProCameraUser.aidl b/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl
index eacb0f4..a987a16 100644
--- a/core/java/android/hardware/IProCameraUser.aidl
+++ b/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package android.hardware;
+package com.android.internal.app;
+
+import android.graphics.Bitmap;
/** @hide */
-interface IProCameraUser
-{
- /**
- * Keep up-to-date with frameworks/av/include/camera/IProCameraUser.h
- */
- void disconnect();
+oneway interface IAssistScreenshotReceiver {
+ void send(in Bitmap screenshot);
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 87b6ed7..bea4ece 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -111,6 +111,7 @@ interface IBatteryStats {
void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void noteNetworkInterfaceType(String iface, int type);
void noteNetworkStatsEnabled();
+ void noteDeviceIdleMode(boolean enabled, boolean fromActive, boolean fromMotion);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 2bf02f1..7ae7d0f 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -23,7 +23,6 @@ import android.widget.Toolbar;
import com.android.internal.R;
import com.android.internal.view.ActionBarPolicy;
-import com.android.internal.view.ActionModeWrapper;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 02f675c..f479f4f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -33,6 +33,7 @@ import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.os.Build;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
@@ -74,6 +75,7 @@ public class NativeLibraryHelper {
final long[] apkHandles;
final boolean multiArch;
+ final boolean extractNativeLibs;
public static Handle create(File packageFile) throws IOException {
try {
@@ -86,14 +88,16 @@ public class NativeLibraryHelper {
public static Handle create(Package pkg) throws IOException {
return create(pkg.getAllCodePaths(),
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
- return create(lite.getAllCodePaths(), lite.multiArch);
+ return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs);
}
- private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
+ private static Handle create(List<String> codePaths, boolean multiArch,
+ boolean extractNativeLibs) throws IOException {
final int size = codePaths.size();
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
@@ -108,12 +112,13 @@ public class NativeLibraryHelper {
}
}
- return new Handle(apkHandles, multiArch);
+ return new Handle(apkHandles, multiArch, extractNativeLibs);
}
- Handle(long[] apkHandles, boolean multiArch) {
+ Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) {
this.apkHandles = apkHandles;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
mGuard.open("close");
}
@@ -146,8 +151,8 @@ public class NativeLibraryHelper {
private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
- private native static int nativeCopyNativeBinaries(long handle,
- String sharedLibraryPath, String abiToCopy);
+ private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
+ String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge);
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
@@ -167,7 +172,8 @@ public class NativeLibraryHelper {
*/
public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
- int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
+ int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
+ handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
if (res != INSTALL_SUCCEEDED) {
return res;
}
@@ -218,7 +224,8 @@ public class NativeLibraryHelper {
/**
* Remove the native binaries of a given package. This deletes the files
*/
- public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
+ public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
+ boolean deleteRootDir) {
if (DEBUG_NATIVE) {
Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
}
@@ -247,7 +254,8 @@ public class NativeLibraryHelper {
// asked to or this will prevent installation of future updates.
if (deleteRootDir) {
if (!nativeLibraryRoot.delete()) {
- Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
+ Slog.w(TAG, "Could not delete native binary directory: " +
+ nativeLibraryRoot.getPath());
}
}
}
@@ -416,6 +424,9 @@ public class NativeLibraryHelper {
// We don't care about the other return values for now.
private static final int BITCODE_PRESENT = 1;
+ private static final boolean HAS_NATIVE_BRIDGE =
+ !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
+
private static native int hasRenderscriptBitcode(long apkHandle);
public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 57fcf57..06bdb24 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -764,17 +764,55 @@ public class InputMethodUtils {
private int[] mCurrentProfileIds = new int[0];
private static void buildEnabledInputMethodsSettingString(
- StringBuilder builder, Pair<String, ArrayList<String>> pair) {
- String id = pair.first;
- ArrayList<String> subtypes = pair.second;
- builder.append(id);
+ StringBuilder builder, Pair<String, ArrayList<String>> ime) {
+ builder.append(ime.first);
// Inputmethod and subtypes are saved in the settings as follows:
// ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
- for (String subtypeId: subtypes) {
+ for (String subtypeId: ime.second) {
builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
}
}
+ public static String buildInputMethodsSettingString(
+ List<Pair<String, ArrayList<String>>> allImeSettingsMap) {
+ final StringBuilder b = new StringBuilder();
+ boolean needsSeparator = false;
+ for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) {
+ if (needsSeparator) {
+ b.append(INPUT_METHOD_SEPARATER);
+ }
+ buildEnabledInputMethodsSettingString(b, ime);
+ needsSeparator = true;
+ }
+ return b.toString();
+ }
+
+ public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
+ String enabledInputMethodsStr,
+ TextUtils.SimpleStringSplitter inputMethodSplitter,
+ TextUtils.SimpleStringSplitter subtypeSplitter) {
+ ArrayList<Pair<String, ArrayList<String>>> imsList =
+ new ArrayList<Pair<String, ArrayList<String>>>();
+ if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+ return imsList;
+ }
+ inputMethodSplitter.setString(enabledInputMethodsStr);
+ while (inputMethodSplitter.hasNext()) {
+ String nextImsStr = inputMethodSplitter.next();
+ subtypeSplitter.setString(nextImsStr);
+ if (subtypeSplitter.hasNext()) {
+ ArrayList<String> subtypeHashes = new ArrayList<String>();
+ // The first element is ime id.
+ String imeId = subtypeSplitter.next();
+ while (subtypeSplitter.hasNext()) {
+ subtypeHashes.add(subtypeSplitter.next());
+ }
+ imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+ }
+ }
+ return imsList;
+ }
+
public InputMethodSettings(
Resources res, ContentResolver resolver,
HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
@@ -875,27 +913,9 @@ public class InputMethodUtils {
}
public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
- ArrayList<Pair<String, ArrayList<String>>> imsList
- = new ArrayList<Pair<String, ArrayList<String>>>();
- final String enabledInputMethodsStr = getEnabledInputMethodsStr();
- if (TextUtils.isEmpty(enabledInputMethodsStr)) {
- return imsList;
- }
- mInputMethodSplitter.setString(enabledInputMethodsStr);
- while (mInputMethodSplitter.hasNext()) {
- String nextImsStr = mInputMethodSplitter.next();
- mSubtypeSplitter.setString(nextImsStr);
- if (mSubtypeSplitter.hasNext()) {
- ArrayList<String> subtypeHashes = new ArrayList<String>();
- // The first element is ime id.
- String imeId = mSubtypeSplitter.next();
- while (mSubtypeSplitter.hasNext()) {
- subtypeHashes.add(mSubtypeSplitter.next());
- }
- imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
- }
- }
- return imsList;
+ return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
+ mInputMethodSplitter,
+ mSubtypeSplitter);
}
public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
new file mode 100644
index 0000000..9e178df
--- /dev/null
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -0,0 +1,7 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.logging;
+
+# interaction logs
+524287 sysui_view_visibility (category|1|5),(visible|1|6)
+524288 sysui_action (category|1|5),(type|1|6)
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
new file mode 100644
index 0000000..e5cba84
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging;
+
+/**
+ * Constants for mestrics logs.
+ *
+ * @hide
+ */
+public interface MetricsConstants {
+ // These constants must match those in the analytic pipeline.
+ public static final int ACCESSIBILITY = 2;
+ public static final int ACCESSIBILITY_CAPTION_PROPERTIES = 3;
+ public static final int ACCESSIBILITY_SERVICE = 4;
+ public static final int ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
+ public static final int ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
+ public static final int ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
+ public static final int ACCOUNT = 8;
+ public static final int ACCOUNTS_ACCOUNT_SYNC = 9;
+ public static final int ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
+ public static final int ACCOUNTS_MANAGE_ACCOUNTS = 11;
+ public static final int APN = 12;
+ public static final int APN_EDITOR = 13;
+ public static final int APPLICATION = 16;
+ public static final int APPLICATIONS_APP_LAUNCH = 17;
+ public static final int APPLICATIONS_APP_PERMISSION = 18;
+ public static final int APPLICATIONS_APP_STORAGE = 19;
+ public static final int APPLICATIONS_INSTALLED_APP_DETAILS = 20;
+ public static final int APPLICATIONS_PROCESS_STATS_DETAIL = 21;
+ public static final int APPLICATIONS_PROCESS_STATS_MEM_DETAIL = 22;
+ public static final int APPLICATIONS_PROCESS_STATS_UI = 23;
+ public static final int APP_OPS_DETAILS = 14;
+ public static final int APP_OPS_SUMMARY = 15;
+ public static final int BLUETOOTH = 24;
+ public static final int BLUETOOTH_DEVICE_PICKER = 25;
+ public static final int BLUETOOTH_DEVICE_PROFILES = 26;
+ public static final int CHOOSE_LOCK_GENERIC = 27;
+ public static final int CHOOSE_LOCK_PASSWORD = 28;
+ public static final int CHOOSE_LOCK_PATTERN = 29;
+ public static final int CONFIRM_LOCK_PASSWORD = 30;
+ public static final int CONFIRM_LOCK_PATTERN = 31;
+ public static final int CRYPT_KEEPER = 32;
+ public static final int CRYPT_KEEPER_CONFIRM = 33;
+ public static final int DASHBOARD_SEARCH_RESULTS = 34;
+ public static final int DASHBOARD_SUMMARY = 35;
+ public static final int DATA_USAGE = 36;
+ public static final int DATA_USAGE_SUMMARY = 37;
+ public static final int DATE_TIME = 38;
+ public static final int DEVELOPMENT = 39;
+ public static final int DEVICEINFO = 40;
+ public static final int DEVICEINFO_IMEI_INFORMATION = 41;
+ public static final int DEVICEINFO_MEMORY = 42;
+ public static final int DEVICEINFO_SIM_STATUS = 43;
+ public static final int DEVICEINFO_STATUS = 44;
+ public static final int DEVICEINFO_USB = 45;
+ public static final int DISPLAY = 46;
+ public static final int DREAM = 47;
+ public static final int ENCRYPTION = 48;
+ public static final int FINGERPRINT = 49;
+ public static final int FINGERPRINT_ENROLL = 50;
+ public static final int FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
+ public static final int FUELGAUGE_BATTERY_SAVER = 52;
+ public static final int FUELGAUGE_POWER_USAGE_DETAIL = 53;
+ public static final int FUELGAUGE_POWER_USAGE_SUMMARY = 54;
+ public static final int HOME = 55;
+ public static final int ICC_LOCK = 56;
+ public static final int INPUTMETHOD_KEYBOARD = 58;
+ public static final int INPUTMETHOD_LANGUAGE = 57;
+ public static final int INPUTMETHOD_SPELL_CHECKERS = 59;
+ public static final int INPUTMETHOD_SUBTYPE_ENABLER = 60;
+ public static final int INPUTMETHOD_USER_DICTIONARY = 61;
+ public static final int INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
+ public static final int LOCATION = 63;
+ public static final int LOCATION_MODE = 64;
+ public static final int MAIN_SETTINGS = 1;
+ public static final int MANAGE_APPLICATIONS = 65;
+ public static final int MASTER_CLEAR = 66;
+ public static final int MASTER_CLEAR_CONFIRM = 67;
+ public static final int NET_DATA_USAGE_METERED = 68;
+ public static final int NFC_BEAM = 69;
+ public static final int NFC_PAYMENT = 70;
+ public static final int NOTIFICATION = 71;
+ public static final int NOTIFICATION_APP_NOTIFICATION = 72;
+ public static final int NOTIFICATION_OTHER_SOUND = 73;
+ public static final int NOTIFICATION_REDACTION = 74;
+ public static final int NOTIFICATION_STATION = 75;
+ public static final int NOTIFICATION_ZEN_MODE = 76;
+ public static final int OWNER_INFO = 77;
+ public static final int PRINT_JOB_SETTINGS = 78;
+ public static final int PRINT_SERVICE_SETTINGS = 79;
+ public static final int PRINT_SETTINGS = 80;
+ public static final int PRIVACY = 81;
+ public static final int PROXY_SELECTOR = 82;
+ public static final int QS_AIRPLANEMODE = 112;
+ public static final int QS_BLUETOOTH = 113;
+ public static final int QS_CAST = 114;
+ public static final int QS_CELLULAR = 115;
+ public static final int QS_COLORINVERSION = 116;
+ public static final int QS_DATAUSAGEDETAIL = 117;
+ public static final int QS_DND = 118;
+ public static final int QS_FLASHLIGHT = 119;
+ public static final int QS_HOTSPOT = 120;
+ public static final int QS_INTENT = 121;
+ public static final int QS_LOCATION = 122;
+ public static final int QS_PANEL = 111;
+ public static final int QS_ROTATIONLOCK = 123;
+ public static final int QS_USERDETAIL = 125;
+ public static final int QS_USERDETAILITE = 124;
+ public static final int QS_WIFI = 126;
+ public static final int RESET_NETWORK = 83;
+ public static final int RESET_NETWORK_CONFIRM = 84;
+ public static final int RUNNING_SERVICE_DETAILS = 85;
+ public static final int SCREEN_PINNING = 86;
+ public static final int SECURITY = 87;
+ public static final int SIM = 88;
+ public static final int TESTING = 89;
+ public static final int TETHER = 90;
+ public static final int TRUSTED_CREDENTIALS = 92;
+ public static final int TRUST_AGENT = 91;
+ public static final int TTS_ENGINE_SETTINGS = 93;
+ public static final int TTS_TEXT_TO_SPEECH = 94;
+ public static final int TYPE_UNKNOWN = 0;
+ public static final int USAGE_ACCESS = 95;
+ public static final int USER = 96;
+ public static final int USERS_APP_RESTRICTIONS = 97;
+ public static final int USER_DETAILS = 98;
+ public static final int VIEW_UNKNOWN = 0;
+ public static final int VOICE_INPUT = 99;
+ public static final int VPN = 100;
+ public static final int WALLPAPER_TYPE = 101;
+ public static final int WFD_WIFI_DISPLAY = 102;
+ public static final int WIFI = 103;
+ public static final int WIFI_ADVANCED = 104;
+ public static final int WIFI_APITEST = 107;
+ public static final int WIFI_CALLING = 105;
+ public static final int WIFI_INFO = 108;
+ public static final int WIFI_P2P = 109;
+ public static final int WIFI_SAVED_ACCESS_POINTS = 106;
+ public static final int WIRELESS = 110;
+}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
new file mode 100644
index 0000000..2de7394
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging;
+
+
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Log all the things.
+ *
+ * @hide
+ */
+public class MetricsLogger implements MetricsConstants {
+ // These constants are temporary, they should migrate to MetricsConstants.
+ public static final int APPLICATIONS_ADVANCED = 132;
+ public static final int LOCATION_SCANNING = 133;
+ public static final int MANAGE_APPLICATIONS_ALL = 134;
+ public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 135;
+
+ public static void visible(Context context, int category) throws IllegalArgumentException {
+ if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+ throw new IllegalArgumentException("Must define metric category");
+ }
+ EventLogTags.writeSysuiViewVisibility(category, 100);
+ }
+
+ public static void hidden(Context context, int category) {
+ if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+ throw new IllegalArgumentException("Must define metric category");
+ }
+ EventLogTags.writeSysuiViewVisibility(category, 0);
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f9b1ca1..93dc995 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,18 +16,14 @@
package com.android.internal.os;
-import static android.net.NetworkStats.UID_ALL;
-import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiManager;
import android.os.BadParcelableException;
@@ -42,8 +38,6 @@ import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.WorkSource;
@@ -65,13 +59,14 @@ import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
+import com.android.server.NetworkManagementSocketTagger;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -109,7 +104,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 119 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 122 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -132,6 +127,9 @@ public final class BatteryStatsImpl extends BatteryStats {
static final int MSG_REPORT_POWER_CHANGE = 2;
static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -160,7 +158,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ public interface ExternalStatsSync {
+ void scheduleSync();
+ }
+
public final MyHandler mHandler;
+ private final ExternalStatsSync mExternalSync;
private BatteryCallback mCallback;
@@ -307,8 +310,14 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mInteractive;
StopwatchTimer mInteractiveTimer;
- boolean mLowPowerModeEnabled;
- StopwatchTimer mLowPowerModeEnabledTimer;
+ boolean mPowerSaveModeEnabled;
+ StopwatchTimer mPowerSaveModeEnabledTimer;
+
+ boolean mDeviceIdling;
+ StopwatchTimer mDeviceIdlingTimer;
+
+ boolean mDeviceIdleModeEnabled;
+ StopwatchTimer mDeviceIdleModeEnabledTimer;
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
@@ -324,7 +333,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
- final StopwatchTimer[] mPhoneSignalStrengthsTimer =
+ final StopwatchTimer[] mPhoneSignalStrengthsTimer =
new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
StopwatchTimer mPhoneSignalScanningTimer;
@@ -411,6 +420,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int mMinDischargeStepLevel;
final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
+ ArrayList<PackageChange> mDailyPackageChanges;
int mLastChargeStepLevel;
int mMaxChargeStepLevel;
@@ -438,18 +448,17 @@ public final class BatteryStatsImpl extends BatteryStats {
private int mLoadedNumConnectivityChange;
private int mUnpluggedNumConnectivityChange;
+ private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
+
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
- private final HashMap<String, SamplingTimer> mKernelWakelockStats =
- new HashMap<String, SamplingTimer>();
+ private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
public Map<String, ? extends Timer> getKernelWakelockStats() {
return mKernelWakelockStats;
}
- private static int sKernelWakelockUpdateVersion = 0;
-
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -458,56 +467,12 @@ public final class BatteryStatsImpl extends BatteryStats {
return mWakeupReasonStats;
}
- private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
- Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
- Process.PROC_QUOTES,
- Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM,
- Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime
- };
-
- private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
- Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name
- Process.PROC_TAB_TERM|Process.PROC_COMBINE|
- Process.PROC_OUT_LONG, // 1: count
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE,
- Process.PROC_TAB_TERM|Process.PROC_COMBINE
- |Process.PROC_OUT_LONG, // 6: totalTime
- };
-
- private final String[] mProcWakelocksName = new String[3];
- private final long[] mProcWakelocksData = new long[3];
-
- /*
- * Used as a buffer for reading in data from /proc/wakelocks before it is processed and added
- * to mKernelWakelockStats.
- */
- private final Map<String, KernelWakelockStats> mProcWakelockFileStats =
- new HashMap<String, KernelWakelockStats>();
-
- private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
- private NetworkStats mCurMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mLastMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mCurWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mLastWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
- private NetworkStats mTmpNetworkStats;
- private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
-
- @GuardedBy("this")
- private String[] mMobileIfaces = new String[0];
- @GuardedBy("this")
- private String[] mWifiIfaces = new String[0];
-
public BatteryStatsImpl() {
mFile = null;
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
+ mExternalSync = null;
clearHistoryLocked();
}
@@ -517,7 +482,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
static class TimeBase {
- private final ArrayList<TimeBaseObs> mObservers = new ArrayList<TimeBaseObs>();
+ private final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
private long mUptime;
private long mRealtime;
@@ -1772,143 +1737,6 @@ public final class BatteryStatsImpl extends BatteryStats {
return timer;
}
- private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
-
- FileInputStream is;
- byte[] buffer = new byte[8192];
- int len;
- boolean wakeup_sources = false;
-
- try {
- try {
- is = new FileInputStream("/proc/wakelocks");
- } catch (java.io.FileNotFoundException e) {
- try {
- is = new FileInputStream("/d/wakeup_sources");
- wakeup_sources = true;
- } catch (java.io.FileNotFoundException e2) {
- return null;
- }
- }
-
- len = is.read(buffer);
- is.close();
- } catch (java.io.IOException e) {
- return null;
- }
-
- if (len > 0) {
- int i;
- for (i=0; i<len; i++) {
- if (buffer[i] == '\0') {
- len = i;
- break;
- }
- }
- }
-
- return parseProcWakelocks(buffer, len, wakeup_sources);
- }
-
- private final Map<String, KernelWakelockStats> parseProcWakelocks(
- byte[] wlBuffer, int len, boolean wakeup_sources) {
- String name;
- int count;
- long totalTime;
- int startIndex;
- int endIndex;
- int numUpdatedWlNames = 0;
-
- // Advance past the first line.
- int i;
- for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
- startIndex = endIndex = i + 1;
-
- synchronized(this) {
- Map<String, KernelWakelockStats> m = mProcWakelockFileStats;
-
- sKernelWakelockUpdateVersion++;
- while (endIndex < len) {
- for (endIndex=startIndex;
- endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
- endIndex++);
- endIndex++; // endIndex is an exclusive upper bound.
- // Don't go over the end of the buffer, Process.parseProcLine might
- // write to wlBuffer[endIndex]
- if (endIndex >= (len - 1) ) {
- return m;
- }
-
- String[] nameStringArray = mProcWakelocksName;
- long[] wlData = mProcWakelocksData;
- // Stomp out any bad characters since this is from a circular buffer
- // A corruption is seen sometimes that results in the vm crashing
- // This should prevent crashes and the line will probably fail to parse
- for (int j = startIndex; j < endIndex; j++) {
- if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
- }
- boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
- wakeup_sources ? WAKEUP_SOURCES_FORMAT :
- PROC_WAKELOCKS_FORMAT,
- nameStringArray, wlData, null);
-
- name = nameStringArray[0];
- count = (int) wlData[1];
-
- if (wakeup_sources) {
- // convert milliseconds to microseconds
- totalTime = wlData[2] * 1000;
- } else {
- // convert nanoseconds to microseconds with rounding.
- totalTime = (wlData[2] + 500) / 1000;
- }
-
- if (parsed && name.length() > 0) {
- if (!m.containsKey(name)) {
- m.put(name, new KernelWakelockStats(count, totalTime,
- sKernelWakelockUpdateVersion));
- numUpdatedWlNames++;
- } else {
- KernelWakelockStats kwlStats = m.get(name);
- if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
- kwlStats.mCount += count;
- kwlStats.mTotalTime += totalTime;
- } else {
- kwlStats.mCount = count;
- kwlStats.mTotalTime = totalTime;
- kwlStats.mVersion = sKernelWakelockUpdateVersion;
- numUpdatedWlNames++;
- }
- }
- }
- startIndex = endIndex;
- }
-
- if (m.size() != numUpdatedWlNames) {
- // Don't report old data.
- Iterator<KernelWakelockStats> itr = m.values().iterator();
- while (itr.hasNext()) {
- if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
- itr.remove();
- }
- }
- }
- return m;
- }
- }
-
- private class KernelWakelockStats {
- public int mCount;
- public long mTotalTime;
- public int mVersion;
-
- KernelWakelockStats(int count, long totalTime, int version) {
- mCount = count;
- mTotalTime = totalTime;
- mVersion = version;
- }
- }
-
/*
* Get the KernelWakelockTimer associated with name, and create a new one if one
* doesn't already exist.
@@ -3380,35 +3208,110 @@ public final class BatteryStatsImpl extends BatteryStats {
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
} else {
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
- updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
+ updateMobileRadioStateLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
}
}
}
- public void noteLowPowerMode(boolean enabled) {
- if (mLowPowerModeEnabled != enabled) {
+ public void notePowerSaveMode(boolean enabled) {
+ if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- mLowPowerModeEnabled = enabled;
+ mPowerSaveModeEnabled = enabled;
if (enabled) {
- mHistoryCur.states2 |= HistoryItem.STATE2_LOW_POWER_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode enabled to: "
+ mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ Integer.toHexString(mHistoryCur.states2));
- mLowPowerModeEnabledTimer.startRunningLocked(elapsedRealtime);
+ mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtime);
} else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_LOW_POWER_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode disabled to: "
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ Integer.toHexString(mHistoryCur.states2));
- mLowPowerModeEnabledTimer.stopRunningLocked(elapsedRealtime);
+ mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
+ public void noteDeviceIdleModeLocked(boolean enabled, boolean fromActive, boolean fromMotion) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ boolean nowIdling = enabled;
+ if (mDeviceIdling && !enabled && !fromActive && !fromMotion) {
+ // We don't go out of general idling mode until explicitly taken out of
+ // device idle through going active or significant motion.
+ nowIdling = true;
+ }
+ if (mDeviceIdling != nowIdling) {
+ mDeviceIdling = nowIdling;
+ int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
+ if (enabled) {
+ mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
+ } else {
+ mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
+ }
+ }
+ if (mDeviceIdleModeEnabled != enabled) {
+ mDeviceIdleModeEnabled = enabled;
+ if (fromMotion) {
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SIGNIFICANT_MOTION,
+ "", 0);
+ }
+ if (fromActive) {
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE,
+ "", 0);
+ }
+ if (enabled) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_DEVICE_IDLE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ mDeviceIdleModeEnabledTimer.startRunningLocked(elapsedRealtime);
+ } else {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_DEVICE_IDLE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ mDeviceIdleModeEnabledTimer.stopRunningLocked(elapsedRealtime);
+ }
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ }
+
+ public void notePackageInstalledLocked(String pkgName, int versionCode) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
+ pkgName, versionCode);
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = pkgName;
+ pc.mUpdate = true;
+ pc.mVersionCode = versionCode;
+ addPackageChange(pc);
+ }
+
+ public void notePackageUninstalledLocked(String pkgName) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
+ pkgName, 0);
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = pkgName;
+ pc.mUpdate = true;
+ addPackageChange(pc);
+ }
+
+ private void addPackageChange(PackageChange pc) {
+ if (mDailyPackageChanges == null) {
+ mDailyPackageChanges = new ArrayList<>();
+ }
+ mDailyPackageChanges.add(pc);
+ }
+
public void notePhoneOnLocked() {
if (!mPhoneOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -3643,6 +3546,7 @@ public final class BatteryStatsImpl extends BatteryStats {
addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3656,6 +3560,7 @@ public final class BatteryStatsImpl extends BatteryStats {
addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3818,6 +3723,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
}
+ scheduleSyncExternalStatsLocked();
} else {
Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
}
@@ -3856,6 +3762,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
}
+ scheduleSyncExternalStatsLocked();
} else {
Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
}
@@ -3870,6 +3777,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mWifiState = wifiState;
mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3941,6 +3849,7 @@ public final class BatteryStatsImpl extends BatteryStats {
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3954,6 +3863,7 @@ public final class BatteryStatsImpl extends BatteryStats {
addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
+ scheduleSyncExternalStatsLocked();
}
}
@@ -3980,6 +3890,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3995,6 +3906,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
@@ -4052,6 +3964,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -4067,6 +3980,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ scheduleSyncExternalStatsLocked();
}
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
@@ -4174,7 +4088,8 @@ public final class BatteryStatsImpl extends BatteryStats {
// During device boot, qtaguid isn't enabled until after the inital
// loading of battery stats. Now that they're enabled, take our initial
// snapshot for future delta calculation.
- updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
+ updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+ updateWifiStateLocked(null);
}
@Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
@@ -4195,12 +4110,28 @@ public final class BatteryStatsImpl extends BatteryStats {
return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
- @Override public long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which) {
- return mLowPowerModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ @Override public long getPowerSaveModeEnabledTime(long elapsedRealtimeUs, int which) {
+ return mPowerSaveModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
- @Override public int getLowPowerModeEnabledCount(int which) {
- return mLowPowerModeEnabledTimer.getCountLocked(which);
+ @Override public int getPowerSaveModeEnabledCount(int which) {
+ return mPowerSaveModeEnabledTimer.getCountLocked(which);
+ }
+
+ @Override public long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which) {
+ return mDeviceIdleModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getDeviceIdleModeEnabledCount(int which) {
+ return mDeviceIdleModeEnabledTimer.getCountLocked(which);
+ }
+
+ @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) {
+ return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getDeviceIdlingCount(int which) {
+ return mDeviceIdlingTimer.getCountLocked(which);
}
@Override public int getNumConnectivityChange(int which) {
@@ -4505,17 +4436,17 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- public Map<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
+ public ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
return mWakelockStats.getMap();
}
@Override
- public Map<String, ? extends BatteryStats.Timer> getSyncStats() {
+ public ArrayMap<String, ? extends BatteryStats.Timer> getSyncStats() {
return mSyncStats.getMap();
}
@Override
- public Map<String, ? extends BatteryStats.Timer> getJobStats() {
+ public ArrayMap<String, ? extends BatteryStats.Timer> getJobStats() {
return mJobStats.getMap();
}
@@ -4525,12 +4456,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
- public Map<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
+ public ArrayMap<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
return mProcessStats;
}
@Override
- public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
+ public ArrayMap<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
return mPackageStats;
}
@@ -5848,7 +5779,7 @@ public final class BatteryStatsImpl extends BatteryStats {
Slog.w(TAG, "File corrupt: too many excessive power entries " + N);
return false;
}
-
+
mExcessivePower = new ArrayList<ExcessivePower>();
for (int i=0; i<N; i++) {
ExcessivePower ew = new ExcessivePower();
@@ -6051,40 +5982,20 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
public final class Pkg extends BatteryStats.Uid.Pkg implements TimeBaseObs {
/**
- * Number of times this package has done something that could wake up the
- * device from sleep.
- */
- int mWakeups;
-
- /**
- * Number of things that could wake up the device loaded from a
- * previous save.
- */
- int mLoadedWakeups;
-
- /**
- * Number of things that could wake up the device as of the
- * last run.
- */
- int mLastWakeups;
-
- /**
- * Number of things that could wake up the device as of the
- * last run.
+ * Number of times wakeup alarms have occurred for this app.
*/
- int mUnpluggedWakeups;
+ ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>();
/**
* The statics we have collected for this package's services.
*/
- final HashMap<String, Serv> mServiceStats = new HashMap<String, Serv>();
+ final ArrayMap<String, Serv> mServiceStats = new ArrayMap<>();
Pkg() {
mOnBatteryScreenOffTimeBase.add(this);
}
public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
- mUnpluggedWakeups = mWakeups;
}
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
@@ -6095,10 +6006,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void readFromParcelLocked(Parcel in) {
- mWakeups = in.readInt();
- mLoadedWakeups = in.readInt();
- mLastWakeups = 0;
- mUnpluggedWakeups = in.readInt();
+ int numWA = in.readInt();
+ mWakeupAlarms.clear();
+ for (int i=0; i<numWA; i++) {
+ String tag = in.readString();
+ mWakeupAlarms.put(tag, new Counter(mOnBatteryTimeBase, in));
+ }
int numServs = in.readInt();
mServiceStats.clear();
@@ -6112,34 +6025,39 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void writeToParcelLocked(Parcel out) {
- out.writeInt(mWakeups);
- out.writeInt(mLoadedWakeups);
- out.writeInt(mUnpluggedWakeups);
-
- out.writeInt(mServiceStats.size());
- for (Map.Entry<String, Uid.Pkg.Serv> servEntry : mServiceStats.entrySet()) {
- out.writeString(servEntry.getKey());
- Uid.Pkg.Serv serv = servEntry.getValue();
-
+ int numWA = mWakeupAlarms.size();
+ out.writeInt(numWA);
+ for (int i=0; i<numWA; i++) {
+ out.writeString(mWakeupAlarms.keyAt(i));
+ mWakeupAlarms.valueAt(i).writeToParcel(out);
+ }
+
+ final int NS = mServiceStats.size();
+ out.writeInt(NS);
+ for (int i=0; i<NS; i++) {
+ out.writeString(mServiceStats.keyAt(i));
+ Uid.Pkg.Serv serv = mServiceStats.valueAt(i);
serv.writeToParcelLocked(out);
}
}
@Override
- public Map<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
- return mServiceStats;
+ public ArrayMap<String, ? extends BatteryStats.Counter> getWakeupAlarmStats() {
+ return mWakeupAlarms;
}
- @Override
- public int getWakeups(int which) {
- int val = mWakeups;
- if (which == STATS_CURRENT) {
- val -= mLoadedWakeups;
- } else if (which == STATS_SINCE_UNPLUGGED) {
- val -= mUnpluggedWakeups;
+ public void noteWakeupAlarmLocked(String tag) {
+ Counter c = mWakeupAlarms.get(tag);
+ if (c == null) {
+ c = new Counter(mOnBatteryTimeBase);
+ mWakeupAlarms.put(tag, c);
}
+ c.stepAtomic();
+ }
- return val;
+ @Override
+ public ArrayMap<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
+ return mServiceStats;
}
/**
@@ -6381,14 +6299,6 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public BatteryStatsImpl getBatteryStats() {
- return BatteryStatsImpl.this;
- }
-
- public void incWakeupsLocked() {
- mWakeups++;
- }
-
final Serv newServiceStatsLocked() {
return new Serv();
}
@@ -6647,7 +6557,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public BatteryStatsImpl(File systemDir, Handler handler) {
+ public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
if (systemDir != null) {
mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
new File(systemDir, "batterystats.bin.tmp"));
@@ -6656,14 +6566,17 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
+ mExternalSync = externalSync;
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase);
}
- mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
- mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
+ mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase);
+ mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
+ mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+ mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null,
@@ -6726,6 +6639,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
+ mExternalSync = null;
clearHistoryLocked();
readFromParcel(p);
}
@@ -6789,6 +6703,11 @@ public final class BatteryStatsImpl extends BatteryStats {
mDailyChargeStepTracker.mNumStepDurations,
mDailyChargeStepTracker.mStepDurations);
}
+ if (mDailyPackageChanges != null) {
+ hasData = true;
+ item.mPackageChanges = mDailyPackageChanges;
+ mDailyPackageChanges = null;
+ }
mDailyDischargeStepTracker.init();
mDailyChargeStepTracker.init();
updateDailyDeadlineLocked();
@@ -6839,6 +6758,21 @@ public final class BatteryStatsImpl extends BatteryStats {
out.attribute(null, "end", Long.toString(dit.mEndTime));
writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
+ if (dit.mPackageChanges != null) {
+ for (int j=0; j<dit.mPackageChanges.size(); j++) {
+ PackageChange pc = dit.mPackageChanges.get(j);
+ if (pc.mUpdate) {
+ out.startTag(null, "upd");
+ out.attribute(null, "pkg", pc.mPackageName);
+ out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+ out.endTag(null, "upd");
+ } else {
+ out.startTag(null, "rem");
+ out.attribute(null, "pkg", pc.mPackageName);
+ out.endTag(null, "rem");
+ }
+ }
+ }
out.endTag(null, "item");
}
out.endTag(null, "daily-items");
@@ -6951,6 +6885,26 @@ public final class BatteryStatsImpl extends BatteryStats {
readDailyItemTagDetailsLocked(parser, dit, false, "dis");
} else if (tagName.equals("chg")) {
readDailyItemTagDetailsLocked(parser, dit, true, "chg");
+ } else if (tagName.equals("upd")) {
+ if (dit.mPackageChanges == null) {
+ dit.mPackageChanges = new ArrayList<>();
+ }
+ PackageChange pc = new PackageChange();
+ pc.mUpdate = true;
+ pc.mPackageName = parser.getAttributeValue(null, "pkg");
+ String verStr = parser.getAttributeValue(null, "ver");
+ pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+ dit.mPackageChanges.add(pc);
+ XmlUtils.skipCurrentTag(parser);
+ } else if (tagName.equals("rem")) {
+ if (dit.mPackageChanges == null) {
+ dit.mPackageChanges = new ArrayList<>();
+ }
+ PackageChange pc = new PackageChange();
+ pc.mUpdate = false;
+ pc.mPackageName = parser.getAttributeValue(null, "pkg");
+ dit.mPackageChanges.add(pc);
+ XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown element under <item>: "
+ parser.getName());
@@ -7233,7 +7187,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mScreenBrightnessTimer[i].reset(false);
}
mInteractiveTimer.reset(false);
- mLowPowerModeEnabledTimer.reset(false);
+ mPowerSaveModeEnabledTimer.reset(false);
+ mDeviceIdleModeEnabledTimer.reset(false);
+ mDeviceIdlingTimer.reset(false);
mPhoneOnTimer.reset(false);
mAudioOnTimer.reset(false);
mVideoOnTimer.reset(false);
@@ -7355,19 +7311,233 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
-
+
public void pullPendingStateUpdatesLocked() {
- updateKernelWakelocksLocked();
- updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
- // TODO(adamlesinski): enable when bluedroid stops deadlocking. b/19248786
- // updateBluetoothControllerActivityLocked();
- updateWifiControllerActivityLocked();
if (mOnBatteryInternal) {
final boolean screenOn = mScreenState == Display.STATE_ON;
updateDischargeScreenLevelsLocked(screenOn, screenOn);
}
}
+ private String[] mMobileIfaces = EmptyArray.STRING;
+ private String[] mWifiIfaces = EmptyArray.STRING;
+
+ private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
+
+ private static final int NETWORK_STATS_LAST = 0;
+ private static final int NETWORK_STATS_NEXT = 1;
+ private static final int NETWORK_STATS_DELTA = 2;
+
+ private final NetworkStats[] mMobileNetworkStats = new NetworkStats[] {
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50)
+ };
+
+ private final NetworkStats[] mWifiNetworkStats = new NetworkStats[] {
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50),
+ new NetworkStats(SystemClock.elapsedRealtime(), 50)
+ };
+
+ /**
+ * Retrieves the delta of network stats for the given network ifaces. Uses networkStatsBuffer
+ * as a buffer of NetworkStats objects to cycle through when computing deltas.
+ */
+ private NetworkStats getNetworkStatsDeltaLocked(String[] ifaces,
+ NetworkStats[] networkStatsBuffer)
+ throws IOException {
+ if (!SystemProperties.getBoolean(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
+ false)) {
+ return null;
+ }
+
+ final NetworkStats stats = mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL,
+ ifaces, NetworkStats.TAG_NONE, networkStatsBuffer[NETWORK_STATS_NEXT]);
+ networkStatsBuffer[NETWORK_STATS_DELTA] = NetworkStats.subtract(stats,
+ networkStatsBuffer[NETWORK_STATS_LAST], null, null,
+ networkStatsBuffer[NETWORK_STATS_DELTA]);
+ networkStatsBuffer[NETWORK_STATS_NEXT] = networkStatsBuffer[NETWORK_STATS_LAST];
+ networkStatsBuffer[NETWORK_STATS_LAST] = stats;
+ return networkStatsBuffer[NETWORK_STATS_DELTA];
+ }
+
+ /**
+ * Distribute WiFi energy info and network traffic to apps.
+ * @param info The energy information from the WiFi controller.
+ */
+ public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
+ final NetworkStats delta;
+ try {
+ delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to get wifi network stats", e);
+ return;
+ }
+
+ if (!mOnBatteryInternal) {
+ return;
+ }
+
+ if (delta != null) {
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
+ + " tx=" + entry.txBytes);
+ }
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) {
+ continue;
+ }
+
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+ entry.txPackets);
+
+ mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
+ }
+
+ if (info != null) {
+ // Update WiFi controller stats.
+ mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+ info.getControllerRxTimeMillis());
+ mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+ info.getControllerTxTimeMillis());
+ mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+ info.getControllerIdleTimeMillis());
+ mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+ info.getControllerEnergyUsed());
+ }
+ }
+
+ /**
+ * Distribute Cell radio energy info and network traffic to apps.
+ */
+ public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
+ final NetworkStats delta;
+
+ try {
+ delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to get mobile network stats", e);
+ return;
+ }
+
+ if (delta == null || !mOnBatteryInternal) {
+ return;
+ }
+
+ long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
+ long totalPackets = delta.getTotalPackets();
+
+ final int size = delta.size();
+ for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+ if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+
+ final Uid u = getUidStatsLocked(mapUid(entry.uid));
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
+ entry.txPackets);
+
+ if (radioTime > 0) {
+ // Distribute total radio active time in to this app.
+ long appPackets = entry.rxPackets + entry.txPackets;
+ long appRadioTime = (radioTime*appPackets)/totalPackets;
+ u.noteMobileRadioActiveTimeLocked(appRadioTime);
+ // Remove this app from the totals, so that we don't lose any time
+ // due to rounding.
+ radioTime -= appRadioTime;
+ totalPackets -= appPackets;
+ }
+
+ mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+ entry.rxPackets);
+ mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+ entry.txPackets);
+ }
+
+ if (radioTime > 0) {
+ // Whoops, there is some radio time we can't blame on an app!
+ mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+ mMobileRadioActiveUnknownCount.addCountLocked(1);
+ }
+ }
+
+ /**
+ * Distribute Bluetooth energy info and network traffic to apps.
+ * @param info The energy information from the bluetooth controller.
+ */
+ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
+ if (info != null && mOnBatteryInternal) {
+ mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+ info.getControllerRxTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+ info.getControllerTxTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+ info.getControllerIdleTimeMillis());
+ mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+ info.getControllerEnergyUsed());
+ }
+ }
+
+ /**
+ * Read and distribute kernel wake lock use across apps.
+ */
+ public void updateKernelWakelocksLocked() {
+ final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
+ mTmpWakelockStats);
+ if (wakelockStats == null) {
+ // Not crashing might make board bringup easier.
+ Slog.w(TAG, "Couldn't get kernel wake lock stats");
+ return;
+ }
+
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+
+ SamplingTimer kwlt = mKernelWakelockStats.get(name);
+ if (kwlt == null) {
+ kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
+ true /* track reported val */);
+ mKernelWakelockStats.put(name, kwlt);
+ }
+ kwlt.updateCurrentReportedCount(kws.mCount);
+ kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
+ kwlt.setUpdateVersion(kws.mVersion);
+ }
+
+ if (wakelockStats.size() != mKernelWakelockStats.size()) {
+ // Set timers to stale if they didn't appear in /proc/wakelocks this time.
+ for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
+ SamplingTimer st = ent.getValue();
+ if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
+ st.setStale();
+ }
+ }
+ }
+ }
+
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
final int oldStatus, final int level) {
boolean doWrite = false;
@@ -7521,340 +7691,132 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ private void scheduleSyncExternalStatsLocked() {
+ if (mExternalSync != null) {
+ mExternalSync.scheduleSync();
+ }
+ }
+
// This should probably be exposed in the API, though it's not critical
- private static final int BATTERY_PLUGGED_NONE = 0;
+ public static final int BATTERY_PLUGGED_NONE = 0;
- public void setBatteryState(int status, int health, int plugType, int level,
+ public void setBatteryStateLocked(int status, int health, int plugType, int level,
int temp, int volt) {
- synchronized(this) {
- final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
- final long uptime = SystemClock.uptimeMillis();
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- if (!mHaveBatteryLevel) {
- mHaveBatteryLevel = true;
- // We start out assuming that the device is plugged in (not
- // on battery). If our first report is now that we are indeed
- // plugged in, then twiddle our state to correctly reflect that
- // since we won't be going through the full setOnBattery().
- if (onBattery == mOnBattery) {
- if (onBattery) {
- mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- } else {
- mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- }
- }
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryLevel = (byte)level;
- mMaxChargeStepLevel = mMinDischargeStepLevel =
- mLastChargeStepLevel = mLastDischargeStepLevel = level;
- } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
- recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
- }
- int oldStatus = mHistoryCur.batteryStatus;
- if (onBattery) {
- mDischargeCurrentLevel = level;
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtime, uptime, true);
- }
- } else if (level < 96) {
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtime, uptime, true);
- }
- }
- mCurrentBatteryLevel = level;
- if (mDischargePlugLevel < 0) {
- mDischargePlugLevel = level;
- }
- if (onBattery != mOnBattery) {
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryHealth = (byte)health;
- mHistoryCur.batteryPlugType = (byte)plugType;
- mHistoryCur.batteryTemperature = (short)temp;
- mHistoryCur.batteryVoltage = (char)volt;
- setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
- } else {
- boolean changed = false;
- if (mHistoryCur.batteryLevel != level) {
- mHistoryCur.batteryLevel = (byte)level;
- changed = true;
- }
- if (mHistoryCur.batteryStatus != status) {
- mHistoryCur.batteryStatus = (byte)status;
- changed = true;
- }
- if (mHistoryCur.batteryHealth != health) {
- mHistoryCur.batteryHealth = (byte)health;
- changed = true;
- }
- if (mHistoryCur.batteryPlugType != plugType) {
- mHistoryCur.batteryPlugType = (byte)plugType;
- changed = true;
- }
- if (temp >= (mHistoryCur.batteryTemperature+10)
- || temp <= (mHistoryCur.batteryTemperature-10)) {
- mHistoryCur.batteryTemperature = (short)temp;
- changed = true;
- }
- if (volt > (mHistoryCur.batteryVoltage+20)
- || volt < (mHistoryCur.batteryVoltage-20)) {
- mHistoryCur.batteryVoltage = (char)volt;
- changed = true;
- }
- if (changed) {
- addHistoryRecordLocked(elapsedRealtime, uptime);
- }
- long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
- | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
- | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+ final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final long uptime = SystemClock.uptimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (!mHaveBatteryLevel) {
+ mHaveBatteryLevel = true;
+ // We start out assuming that the device is plugged in (not
+ // on battery). If our first report is now that we are indeed
+ // plugged in, then twiddle our state to correctly reflect that
+ // since we won't be going through the full setOnBattery().
+ if (onBattery == mOnBattery) {
if (onBattery) {
- if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
- mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
- modeBits, elapsedRealtime);
- mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
- modeBits, elapsedRealtime);
- mLastDischargeStepLevel = level;
- mMinDischargeStepLevel = level;
- mInitStepMode = mCurStepMode;
- mModStepMode = 0;
- }
+ mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
} else {
- if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
- mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
- modeBits, elapsedRealtime);
- mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
- modeBits, elapsedRealtime);
- mLastChargeStepLevel = level;
- mMaxChargeStepLevel = level;
- mInitStepMode = mCurStepMode;
- mModStepMode = 0;
- }
+ mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
}
}
- if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
- // We don't record history while we are plugged in and fully charged.
- // The next time we are unplugged, history will be cleared.
- mRecordingHistory = DEBUG;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryLevel = (byte)level;
+ mMaxChargeStepLevel = mMinDischargeStepLevel =
+ mLastChargeStepLevel = mLastDischargeStepLevel = level;
+ } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
+ recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
+ }
+ int oldStatus = mHistoryCur.batteryStatus;
+ if (onBattery) {
+ mDischargeCurrentLevel = level;
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
+ } else if (level < 96) {
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
}
}
- }
-
- public void updateKernelWakelocksLocked() {
- Map<String, KernelWakelockStats> m = readKernelWakelockStats();
-
- if (m == null) {
- // Not crashing might make board bringup easier.
- Slog.w(TAG, "Couldn't get kernel wake lock stats");
- return;
+ mCurrentBatteryLevel = level;
+ if (mDischargePlugLevel < 0) {
+ mDischargePlugLevel = level;
}
+ if (onBattery != mOnBattery) {
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryHealth = (byte)health;
+ mHistoryCur.batteryPlugType = (byte)plugType;
+ mHistoryCur.batteryTemperature = (short)temp;
+ mHistoryCur.batteryVoltage = (char)volt;
+ setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
+ } else {
+ boolean changed = false;
+ if (mHistoryCur.batteryLevel != level) {
+ mHistoryCur.batteryLevel = (byte)level;
+ changed = true;
- for (Map.Entry<String, KernelWakelockStats> ent : m.entrySet()) {
- String name = ent.getKey();
- KernelWakelockStats kws = ent.getValue();
-
- SamplingTimer kwlt = mKernelWakelockStats.get(name);
- if (kwlt == null) {
- kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
- true /* track reported val */);
- mKernelWakelockStats.put(name, kwlt);
+ // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
+ // which will pull external stats.
+ scheduleSyncExternalStatsLocked();
}
- kwlt.updateCurrentReportedCount(kws.mCount);
- kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
- kwlt.setUpdateVersion(sKernelWakelockUpdateVersion);
- }
-
- if (m.size() != mKernelWakelockStats.size()) {
- // Set timers to stale if they didn't appear in /proc/wakelocks this time.
- for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
- SamplingTimer st = ent.getValue();
- if (st.getUpdateVersion() != sKernelWakelockUpdateVersion) {
- st.setStale();
- }
+ if (mHistoryCur.batteryStatus != status) {
+ mHistoryCur.batteryStatus = (byte)status;
+ changed = true;
}
- }
- }
-
- static final int NET_UPDATE_MOBILE = 1<<0;
- static final int NET_UPDATE_WIFI = 1<<1;
- static final int NET_UPDATE_ALL = 0xffff;
-
- private void updateNetworkActivityLocked(int which, long elapsedRealtimeMs) {
- if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
-
- if ((which&NET_UPDATE_MOBILE) != 0 && mMobileIfaces.length > 0) {
- final NetworkStats snapshot;
- final NetworkStats last = mCurMobileSnapshot;
- try {
- snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
- mMobileIfaces, NetworkStats.TAG_NONE, mLastMobileSnapshot);
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to read mobile network stats", e);
- return;
+ if (mHistoryCur.batteryHealth != health) {
+ mHistoryCur.batteryHealth = (byte)health;
+ changed = true;
}
-
- mCurMobileSnapshot = snapshot;
- mLastMobileSnapshot = last;
-
- if (mOnBatteryInternal) {
- final NetworkStats delta = NetworkStats.subtract(snapshot, last,
- null, null, mTmpNetworkStats);
- mTmpNetworkStats = delta;
-
- long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(
- elapsedRealtimeMs);
- long totalPackets = delta.getTotalPackets();
-
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
- final Uid u = getUidStatsLocked(mapUid(entry.uid));
- u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
- entry.txPackets);
-
- if (radioTime > 0) {
- // Distribute total radio active time in to this app.
- long appPackets = entry.rxPackets + entry.txPackets;
- long appRadioTime = (radioTime*appPackets)/totalPackets;
- u.noteMobileRadioActiveTimeLocked(appRadioTime);
- // Remove this app from the totals, so that we don't lose any time
- // due to rounding.
- radioTime -= appRadioTime;
- totalPackets -= appPackets;
- }
-
- mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
- entry.rxBytes);
- mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
- entry.txBytes);
- mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
- entry.rxPackets);
- mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
- entry.txPackets);
- }
-
- if (radioTime > 0) {
- // Whoops, there is some radio time we can't blame on an app!
- mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
- mMobileRadioActiveUnknownCount.addCountLocked(1);
- }
+ if (mHistoryCur.batteryPlugType != plugType) {
+ mHistoryCur.batteryPlugType = (byte)plugType;
+ changed = true;
}
- }
-
- if ((which&NET_UPDATE_WIFI) != 0 && mWifiIfaces.length > 0) {
- final NetworkStats snapshot;
- final NetworkStats last = mCurWifiSnapshot;
- try {
- snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
- mWifiIfaces, NetworkStats.TAG_NONE, mLastWifiSnapshot);
- } catch (IOException e) {
- Log.wtf(TAG, "Failed to read wifi network stats", e);
- return;
+ if (temp >= (mHistoryCur.batteryTemperature+10)
+ || temp <= (mHistoryCur.batteryTemperature-10)) {
+ mHistoryCur.batteryTemperature = (short)temp;
+ changed = true;
}
-
- mCurWifiSnapshot = snapshot;
- mLastWifiSnapshot = last;
-
- if (mOnBatteryInternal) {
- final NetworkStats delta = NetworkStats.subtract(snapshot, last,
- null, null, mTmpNetworkStats);
- mTmpNetworkStats = delta;
-
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
- if (DEBUG) {
- final NetworkStats.Entry cur = snapshot.getValues(i, null);
- Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
- + " tx=" + entry.txBytes + ", cur rx=" + cur.rxBytes
- + " tx=" + cur.txBytes);
- }
-
- if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
- final Uid u = getUidStatsLocked(mapUid(entry.uid));
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
- entry.txPackets);
-
- mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxBytes);
- mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txBytes);
- mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxPackets);
- mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txPackets);
+ if (volt > (mHistoryCur.batteryVoltage+20)
+ || volt < (mHistoryCur.batteryVoltage-20)) {
+ mHistoryCur.batteryVoltage = (char)volt;
+ changed = true;
+ }
+ if (changed) {
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
+ | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
+ | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+ if (onBattery) {
+ if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
+ mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+ modeBits, elapsedRealtime);
+ mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+ modeBits, elapsedRealtime);
+ mLastDischargeStepLevel = level;
+ mMinDischargeStepLevel = level;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
+ }
+ } else {
+ if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
+ mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+ modeBits, elapsedRealtime);
+ mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+ modeBits, elapsedRealtime);
+ mLastChargeStepLevel = level;
+ mMaxChargeStepLevel = level;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
}
}
}
- }
-
- private void updateBluetoothControllerActivityLocked() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter == null) {
- return;
- }
-
- // We read the data even if we are not on battery. Each read clears
- // the previous data, so we must always read to make sure the
- // data is for the current interval.
- BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
- BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
- if (info == null || !info.isValid() || !mOnBatteryInternal) {
- // Bad info or we are not on battery.
- return;
+ if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
+ // We don't record history while we are plugged in and fully charged.
+ // The next time we are unplugged, history will be cleared.
+ mRecordingHistory = DEBUG;
}
-
- mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
- info.getControllerRxTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
- info.getControllerTxTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
- info.getControllerIdleTimeMillis());
- mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
- info.getControllerEnergyUsed());
- }
-
- private void updateWifiControllerActivityLocked() {
- IWifiManager wifiManager = IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
- if (wifiManager == null) {
- return;
- }
-
- WifiActivityEnergyInfo info;
- try {
- // We read the data even if we are not on battery. Each read clears
- // the previous data, so we must always read to make sure the
- // data is for the current interval.
- info = wifiManager.reportActivityInfo();
- } catch (RemoteException e) {
- // Nothing to report, WiFi is dead.
- return;
- }
-
- if (info == null || !info.isValid() || !mOnBatteryInternal) {
- // Bad info or we are not on battery.
- return;
- }
-
- mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
- info.getControllerRxTimeMillis());
- mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
- info.getControllerTxTimeMillis());
- mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
- info.getControllerIdleTimeMillis());
- mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
- info.getControllerEnergyUsed());
}
public long getAwakeTimeBattery() {
@@ -8020,6 +7982,11 @@ public final class BatteryStatsImpl extends BatteryStats {
return mDailyChargeStepTracker;
}
+ @Override
+ public ArrayList<PackageChange> getDailyPackageChanges() {
+ return mDailyPackageChanges;
+ }
+
long getBatteryUptimeLocked() {
return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
}
@@ -8520,6 +8487,20 @@ public final class BatteryStatsImpl extends BatteryStats {
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
+ int NPKG = in.readInt();
+ if (NPKG > 0) {
+ mDailyPackageChanges = new ArrayList<>(NPKG);
+ while (NPKG > 0) {
+ NPKG--;
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = in.readString();
+ pc.mUpdate = in.readInt() != 0;
+ pc.mVersionCode = in.readInt();
+ mDailyPackageChanges.add(pc);
+ }
+ } else {
+ mDailyPackageChanges = null;
+ }
mDailyStartTime = in.readLong();
mNextMinDailyDeadline = in.readLong();
mNextMaxDailyDeadline = in.readLong();
@@ -8534,7 +8515,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mInteractive = false;
mInteractiveTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
- mLowPowerModeEnabledTimer.readSummaryFromParcelLocked(in);
+ mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
+ mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in);
+ mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
mPhoneOnTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
@@ -8769,7 +8752,18 @@ public final class BatteryStatsImpl extends BatteryStats {
for (int ip = 0; ip < NP; ip++) {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
- p.mWakeups = p.mLoadedWakeups = in.readInt();
+ final int NWA = in.readInt();
+ if (NWA > 1000) {
+ Slog.w(TAG, "File corrupt: too many wakeup alarms " + NWA);
+ return;
+ }
+ p.mWakeupAlarms.clear();
+ for (int iwa=0; iwa<NWA; iwa++) {
+ String tag = in.readString();
+ Counter c = new Counter(mOnBatteryTimeBase);
+ c.readSummaryFromParcelLocked(in);
+ p.mWakeupAlarms.put(tag, c);
+ }
NS = in.readInt();
if (NS > 1000) {
Slog.w(TAG, "File corrupt: too many services " + NS);
@@ -8826,6 +8820,18 @@ public final class BatteryStatsImpl extends BatteryStats {
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
+ if (mDailyPackageChanges != null) {
+ final int NPKG = mDailyPackageChanges.size();
+ out.writeInt(NPKG);
+ for (int i=0; i<NPKG; i++) {
+ PackageChange pc = mDailyPackageChanges.get(i);
+ out.writeString(pc.mPackageName);
+ out.writeInt(pc.mUpdate ? 1 : 0);
+ out.writeInt(pc.mVersionCode);
+ }
+ } else {
+ out.writeInt(0);
+ }
out.writeLong(mDailyStartTime);
out.writeLong(mNextMinDailyDeadline);
out.writeLong(mNextMaxDailyDeadline);
@@ -8835,7 +8841,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mLowPowerModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -9080,20 +9088,22 @@ public final class BatteryStatsImpl extends BatteryStats {
: u.mPackageStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Pkg ps = ent.getValue();
- out.writeInt(ps.mWakeups);
+ final int NWA = ps.mWakeupAlarms.size();
+ out.writeInt(NWA);
+ for (int iwa=0; iwa<NWA; iwa++) {
+ out.writeString(ps.mWakeupAlarms.keyAt(iwa));
+ ps.mWakeupAlarms.valueAt(iwa).writeSummaryFromParcelLocked(out);
+ }
NS = ps.mServiceStats.size();
out.writeInt(NS);
- if (NS > 0) {
- for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg.Serv> sent
- : ps.mServiceStats.entrySet()) {
- out.writeString(sent.getKey());
- BatteryStatsImpl.Uid.Pkg.Serv ss = sent.getValue();
- long time = ss.getStartTimeToNowLocked(
- mOnBatteryTimeBase.getUptime(NOW_SYS));
- out.writeLong(time);
- out.writeInt(ss.mStarts);
- out.writeInt(ss.mLaunches);
- }
+ for (int is=0; is<NS; is++) {
+ out.writeString(ps.mServiceStats.keyAt(is));
+ BatteryStatsImpl.Uid.Pkg.Serv ss = ps.mServiceStats.valueAt(is);
+ long time = ss.getStartTimeToNowLocked(
+ mOnBatteryTimeBase.getUptime(NOW_SYS));
+ out.writeLong(time);
+ out.writeInt(ss.mStarts);
+ out.writeInt(ss.mLaunches);
}
}
}
@@ -9132,9 +9142,11 @@ public final class BatteryStatsImpl extends BatteryStats {
in);
}
mInteractive = false;
- mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
+ mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase, in);
mPhoneOn = false;
- mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
+ mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
+ mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+ mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
@@ -9299,7 +9311,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
mInteractiveTimer.writeToParcel(out, uSecRealtime);
- mLowPowerModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
mPhoneOnTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
@@ -9436,8 +9450,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
pr.println("*** Interactive timer:");
mInteractiveTimer.logState(pr, " ");
- pr.println("*** Low power mode timer:");
- mLowPowerModeEnabledTimer.logState(pr, " ");
+ pr.println("*** Power save mode timer:");
+ mPowerSaveModeEnabledTimer.logState(pr, " ");
+ pr.println("*** Device idle mode timer:");
+ mDeviceIdleModeEnabledTimer.logState(pr, " ");
+ pr.println("*** Device idling timer:");
+ mDeviceIdlingTimer.logState(pr, " ");
pr.println("*** Phone timer:");
mPhoneOnTimer.logState(pr, " ");
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
new file mode 100644
index 0000000..768d586
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.Process;
+import android.util.Slog;
+
+import java.io.FileInputStream;
+import java.util.Iterator;
+
+/**
+ * Reads and parses wakelock stats from the kernel (/proc/wakelocks).
+ */
+public class KernelWakelockReader {
+ private static final String TAG = "KernelWakelockReader";
+ private static int sKernelWakelockUpdateVersion = 0;
+ private static final String sWakelockFile = "/proc/wakelocks";
+ private static final String sWakeupSourceFile = "/d/wakeup_sources";
+
+ private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
+ Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
+ Process.PROC_QUOTES,
+ Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM,
+ Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime
+ };
+
+ private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
+ Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE|
+ Process.PROC_OUT_LONG, // 1: count
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+ Process.PROC_TAB_TERM|Process.PROC_COMBINE
+ |Process.PROC_OUT_LONG, // 6: totalTime
+ };
+
+ private final String[] mProcWakelocksName = new String[3];
+ private final long[] mProcWakelocksData = new long[3];
+
+ /**
+ * Reads kernel wakelock stats and updates the staleStats with the new information.
+ * @param staleStats Existing object to update.
+ * @return the updated data.
+ */
+ public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
+ byte[] buffer = new byte[32*1024];
+ int len;
+ boolean wakeup_sources;
+
+ try {
+ FileInputStream is;
+ try {
+ is = new FileInputStream(sWakeupSourceFile);
+ wakeup_sources = true;
+ } catch (java.io.FileNotFoundException e) {
+ try {
+ is = new FileInputStream(sWakelockFile);
+ wakeup_sources = false;
+ } catch (java.io.FileNotFoundException e2) {
+ return null;
+ }
+ }
+
+ len = is.read(buffer);
+ is.close();
+ } catch (java.io.IOException e) {
+ return null;
+ }
+
+ if (len > 0) {
+ if (len >= buffer.length) {
+ Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+ }
+ int i;
+ for (i=0; i<len; i++) {
+ if (buffer[i] == '\0') {
+ len = i;
+ break;
+ }
+ }
+ }
+ return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+ }
+
+ /**
+ * Reads the wakelocks and updates the staleStats with the new information.
+ */
+ private KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources,
+ final KernelWakelockStats staleStats) {
+ String name;
+ int count;
+ long totalTime;
+ int startIndex;
+ int endIndex;
+ int numUpdatedWlNames = 0;
+
+ // Advance past the first line.
+ int i;
+ for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
+ startIndex = endIndex = i + 1;
+
+ synchronized(this) {
+ sKernelWakelockUpdateVersion++;
+ while (endIndex < len) {
+ for (endIndex=startIndex;
+ endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
+ endIndex++);
+ endIndex++; // endIndex is an exclusive upper bound.
+ // Don't go over the end of the buffer, Process.parseProcLine might
+ // write to wlBuffer[endIndex]
+ if (endIndex >= (len - 1) ) {
+ return staleStats;
+ }
+
+ String[] nameStringArray = mProcWakelocksName;
+ long[] wlData = mProcWakelocksData;
+ // Stomp out any bad characters since this is from a circular buffer
+ // A corruption is seen sometimes that results in the vm crashing
+ // This should prevent crashes and the line will probably fail to parse
+ for (int j = startIndex; j < endIndex; j++) {
+ if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
+ }
+ boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
+ wakeup_sources ? WAKEUP_SOURCES_FORMAT :
+ PROC_WAKELOCKS_FORMAT,
+ nameStringArray, wlData, null);
+
+ name = nameStringArray[0];
+ count = (int) wlData[1];
+
+ if (wakeup_sources) {
+ // convert milliseconds to microseconds
+ totalTime = wlData[2] * 1000;
+ } else {
+ // convert nanoseconds to microseconds with rounding.
+ totalTime = (wlData[2] + 500) / 1000;
+ }
+
+ if (parsed && name.length() > 0) {
+ if (!staleStats.containsKey(name)) {
+ staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
+ sKernelWakelockUpdateVersion));
+ numUpdatedWlNames++;
+ } else {
+ KernelWakelockStats.Entry kwlStats = staleStats.get(name);
+ if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
+ kwlStats.mCount += count;
+ kwlStats.mTotalTime += totalTime;
+ } else {
+ kwlStats.mCount = count;
+ kwlStats.mTotalTime = totalTime;
+ kwlStats.mVersion = sKernelWakelockUpdateVersion;
+ numUpdatedWlNames++;
+ }
+ }
+ }
+ startIndex = endIndex;
+ }
+
+ if (staleStats.size() != numUpdatedWlNames) {
+ // Don't report old data.
+ Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
+ while (itr.hasNext()) {
+ if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
+ itr.remove();
+ }
+ }
+ }
+
+ staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
+ return staleStats;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockStats.java b/core/java/com/android/internal/os/KernelWakelockStats.java
new file mode 100644
index 0000000..144ea00
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockStats.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import java.util.HashMap;
+
+/**
+ * Kernel wakelock stats object.
+ */
+public class KernelWakelockStats extends HashMap<String, KernelWakelockStats.Entry> {
+ public static class Entry {
+ public int mCount;
+ public long mTotalTime;
+ public int mVersion;
+
+ Entry(int count, long totalTime, int version) {
+ mCount = count;
+ mTotalTime = totalTime;
+ mVersion = version;
+ }
+ }
+
+ int kernelWakelockVersion;
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fced092..8674a21 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -34,10 +34,13 @@ public final class Zygote {
public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
/** enable Java programming language "assert" statements */
public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
- /** disable the JIT compiler */
+ /** disable the AOT compiler and JIT */
public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
/** Enable logging of third-party JNI activity. */
public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
+ /** enable the JIT compiler */
+ public static final int DEBUG_ENABLE_JIT = 1 << 5;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = 0;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 7acd8f5..4d405b2 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -321,7 +321,7 @@ class ZygoteConnection {
/**
* From --enable-debugger, --enable-checkjni, --enable-assert,
- * --enable-safemode, and --enable-jni-logging.
+ * --enable-safemode, --enable-jit, and --enable-jni-logging.
*/
int debugFlags;
@@ -431,6 +431,8 @@ class ZygoteConnection {
debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
} else if (arg.equals("--enable-checkjni")) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+ } else if (arg.equals("--enable-jit")) {
+ debugFlags |= Zygote.DEBUG_ENABLE_JIT;
} else if (arg.equals("--enable-jni-logging")) {
debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
} else if (arg.equals("--enable-assert")) {
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index d9ebc25..a106f48 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -52,6 +52,7 @@ public class Protocol {
public static final int BASE_WIFI_RTT_SERVICE = 0x00027300;
public static final int BASE_WIFI_PASSPOINT_MANAGER = 0x00028000;
public static final int BASE_WIFI_PASSPOINT_SERVICE = 0x00028100;
+ public static final int BASE_WIFI_LOGGER = 0x00028300;
public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
new file mode 100644
index 0000000..1bcc7a0
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -0,0 +1,48 @@
+package com.android.internal.util;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewRootImpl;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class ScreenShapeHelper {
+ private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
+
+ /**
+ * Return the bottom pixel window outset of a window given its style attributes.
+ * @param displayMetrics Display metrics of the current device
+ * @param windowStyle Window style attributes for the window.
+ * @return An outset dimension in pixels or 0 if no outset should be applied.
+ */
+ public static int getWindowOutsetBottomPx(DisplayMetrics displayMetrics,
+ TypedArray windowStyle) {
+ if (IS_EMULATOR) {
+ return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
+ } else if (windowStyle.hasValue(R.styleable.Window_windowOutsetBottom)) {
+ TypedValue outsetBottom = new TypedValue();
+ windowStyle.getValue(R.styleable.Window_windowOutsetBottom, outsetBottom);
+ return (int) outsetBottom.getDimension(displayMetrics);
+ }
+ return 0;
+ }
+
+ /**
+ * Get whether a device has has a round screen.
+ */
+ public static boolean getWindowIsRound(Resources resources) {
+ if (IS_EMULATOR) {
+ return SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
+ } else {
+ return resources.getBoolean(
+ com.android.internal.R.bool.config_windowIsRound);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index c69d14f..daf745f 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -70,8 +70,8 @@ public class UserIcons {
// Return colored icon instead
colorResId = USER_ICON_COLORS[userId % USER_ICON_COLORS.length];
}
- Drawable icon = Resources.getSystem().getDrawable(R.drawable.ic_account_circle).mutate();
- icon.setColorFilter(Resources.getSystem().getColor(colorResId), Mode.SRC_IN);
+ Drawable icon = Resources.getSystem().getDrawable(R.drawable.ic_account_circle, null).mutate();
+ icon.setColorFilter(Resources.getSystem().getColor(colorResId, null), Mode.SRC_IN);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
return icon;
}
diff --git a/core/java/com/android/internal/view/ActionModeWrapper.java b/core/java/com/android/internal/view/ActionModeWrapper.java
deleted file mode 100644
index d98617d..0000000
--- a/core/java/com/android/internal/view/ActionModeWrapper.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import android.content.Context;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.android.internal.view.menu.MenuBuilder;
-
-/**
- * ActionMode implementation that wraps several actions modes and creates them on the fly depending
- * on the ActionMode type chosen by the client.
- */
-public class ActionModeWrapper extends ActionMode {
-
- /**
- * Interface to defer the ActionMode creation until the type is chosen.
- */
- public interface ActionModeProvider {
- /**
- * Create the desired ActionMode, that will immediately be used as the current active mode
- * in the decorator.
- *
- * @param callback The {@link ActionMode.Callback} to be used.
- * @param menuBuilder The {@link MenuBuilder} that should be used by the created
- * {@link ActionMode}. This will already have been populated.
- * @return A new {@link ActionMode} ready to be used that uses menuBuilder as its menu.
- */
- ActionMode createActionMode(ActionMode.Callback callback, MenuBuilder menuBuilder);
- }
-
- private ActionMode mActionMode;
- private final Context mContext;
- private MenuBuilder mMenu;
- private final ActionMode.Callback mCallback;
- private boolean mTypeLocked = false;
-
- private CharSequence mTitle;
- private CharSequence mSubtitle;
- private View mCustomView;
-
- private final ActionModeProvider mActionModeProvider;
-
- public ActionModeWrapper(
- Context context, ActionMode.Callback callback, ActionModeProvider actionModeProvider) {
- mContext = context;
- mMenu = new MenuBuilder(context).setDefaultShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
- mCallback = callback;
- mActionModeProvider = actionModeProvider;
- }
-
- @Override
- public void setTitle(CharSequence title) {
- if (mActionMode != null) {
- mActionMode.setTitle(title);
- } else {
- mTitle = title;
- }
- }
-
- @Override
- public void setTitle(int resId) {
- if (mActionMode != null) {
- mActionMode.setTitle(resId);
- } else {
- mTitle = resId != 0 ? mContext.getString(resId) : null;
- }
- }
-
- @Override
- public void setSubtitle(CharSequence subtitle) {
- if (mActionMode != null) {
- mActionMode.setSubtitle(subtitle);
- } else {
- mSubtitle = subtitle;
- }
- }
-
- @Override
- public void setSubtitle(int resId) {
- if (mActionMode != null) {
- mActionMode.setSubtitle(resId);
- } else {
- mSubtitle = resId != 0 ? mContext.getString(resId) : null;
- }
- }
-
- @Override
- public void setCustomView(View view) {
- if (mActionMode != null) {
- mActionMode.setCustomView(view);
- } else {
- mCustomView = view;
- }
- }
-
- public ActionMode getWrappedActionMode() {
- return mActionMode;
- }
-
- /**
- * Set the current type as final and create the necessary ActionMode. After this call, any
- * changes to the ActionMode type will be ignored.
- */
- public void lockType() {
- mTypeLocked = true;
- switch (getType()) {
- case ActionMode.TYPE_PRIMARY:
- default:
- mActionMode = mActionModeProvider.createActionMode(mCallback, mMenu);
- break;
- case ActionMode.TYPE_FLOATING:
- // Not implemented yet.
- break;
- }
-
- if (mActionMode == null) {
- return;
- }
-
- mActionMode.setTitle(mTitle);
- mActionMode.setSubtitle(mSubtitle);
- if (mCustomView != null) {
- mActionMode.setCustomView(mCustomView);
- }
-
- mTitle = null;
- mSubtitle = null;
- mCustomView = null;
- }
-
- @Override
- public void setType(int type) {
- if (!mTypeLocked) {
- super.setType(type);
- } else {
- throw new IllegalStateException(
- "You can't change the ActionMode's type after onCreateActionMode.");
- }
- }
-
- @Override
- public void invalidate() {
- if (mActionMode != null) {
- mActionMode.invalidate();
- }
- }
-
- @Override
- public void finish() {
- if (mActionMode != null) {
- mActionMode.finish();
- } else {
- mCallback.onDestroyActionMode(this);
- }
- }
-
- @Override
- public Menu getMenu() {
- return mMenu;
- }
-
- @Override
- public CharSequence getTitle() {
- if (mActionMode != null) {
- return mActionMode.getTitle();
- }
- return mTitle;
- }
-
- @Override
- public CharSequence getSubtitle() {
- if (mActionMode != null) {
- return mActionMode.getSubtitle();
- }
- return mSubtitle;
- }
-
- @Override
- public View getCustomView() {
- if (mActionMode != null) {
- return mActionMode.getCustomView();
- }
- return mCustomView;
- }
-
- @Override
- public MenuInflater getMenuInflater() {
- return new MenuInflater(mContext);
- }
-
-}
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
deleted file mode 100644
index f97a5d1..0000000
--- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ViewAnimator;
-
-/**
- * @hide
- */
-public class AccessibleDateAnimator extends ViewAnimator {
- private long mDateMillis;
-
- public AccessibleDateAnimator(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setDateMillis(long dateMillis) {
- mDateMillis = dateMillis;
- }
-
- /**
- * Announce the currently-selected date when launched.
- */
- @Override
- public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- // Clear the event's current text so that only the current date will be spoken.
- event.getText().clear();
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
- DateUtils.FORMAT_SHOW_WEEKDAY;
-
- String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
- event.getText().add(dateString);
- return true;
- }
- return super.dispatchPopulateAccessibilityEventInternal(event);
- }
-}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
new file mode 100644
index 0000000..be9945d
--- /dev/null
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A floating toolbar for showing contextual menu items.
+ * This view shows as many menu item buttons as can fit in the horizontal toolbar and the
+ * the remaining menu items in a vertical overflow view when the overflow button is clicked.
+ * The horizontal toolbar morphs into the vertical overflow view.
+ */
+public final class FloatingToolbar {
+
+ private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
+ new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return false;
+ }
+ };
+
+ private final Context mContext;
+ private final FloatingToolbarPopup mPopup;
+ private final ViewGroup mMenuItemButtonsContainer;
+ private final View.OnClickListener mMenuItemButtonOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (v.getTag() instanceof MenuItem) {
+ mMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag());
+ mPopup.dismiss();
+ }
+ }
+ };
+
+ private final Rect mContentRect = new Rect();
+ private final Point mCoordinates = new Point();
+
+ private Menu mMenu;
+ private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+ private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+ private View mOpenOverflowButton;
+
+ private int mSuggestedWidth;
+
+ /**
+ * Initializes a floating toolbar.
+ */
+ public FloatingToolbar(Context context, Window window) {
+ mContext = Preconditions.checkNotNull(context);
+ mPopup = new FloatingToolbarPopup(Preconditions.checkNotNull(window.getDecorView()));
+ mMenuItemButtonsContainer = createMenuButtonsContainer(context);
+ }
+
+ /**
+ * Sets the menu to be shown in this floating toolbar.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setMenu(Menu menu) {
+ mMenu = Preconditions.checkNotNull(menu);
+ return this;
+ }
+
+ /**
+ * Sets the custom listener for invocation of menu items in this floating
+ * toolbar.
+ */
+ public FloatingToolbar setOnMenuItemClickListener(
+ MenuItem.OnMenuItemClickListener menuItemClickListener) {
+ if (menuItemClickListener != null) {
+ mMenuItemClickListener = menuItemClickListener;
+ } else {
+ mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the content rectangle. This is the area of the interesting content that this toolbar
+ * should avoid obstructing.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setContentRect(Rect rect) {
+ mContentRect.set(Preconditions.checkNotNull(rect));
+ return this;
+ }
+
+ /**
+ * Sets the suggested width of this floating toolbar.
+ * The actual width will be about this size but there are no guarantees that it will be exactly
+ * the suggested width.
+ * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+ * toolbar.
+ */
+ public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
+ mSuggestedWidth = suggestedWidth;
+ return this;
+ }
+
+ /**
+ * Shows this floating toolbar.
+ */
+ public FloatingToolbar show() {
+ List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+ if (hasContentChanged(menuItems) || hasWidthChanged()) {
+ mPopup.dismiss();
+ layoutMenuItemButtons(menuItems);
+ mShowingTitles = getMenuItemTitles(menuItems);
+ }
+ refreshCoordinates();
+ mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y);
+ if (!mPopup.isShowing()) {
+ mPopup.show(mCoordinates.x, mCoordinates.y);
+ }
+ return this;
+ }
+
+ /**
+ * Updates this floating toolbar to reflect recent position and view updates.
+ * NOTE: This method is a no-op if the toolbar isn't showing.
+ */
+ public FloatingToolbar updateLayout() {
+ if (mPopup.isShowing()) {
+ // show() performs all the logic we need here.
+ show();
+ }
+ return this;
+ }
+
+ /**
+ * Dismisses this floating toolbar.
+ */
+ public void dismiss() {
+ mPopup.dismiss();
+ }
+
+ /**
+ * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+ */
+ public boolean isShowing() {
+ return mPopup.isShowing();
+ }
+
+ /**
+ * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}.
+ */
+ private void refreshCoordinates() {
+ int popupWidth = mPopup.getWidth();
+ int popupHeight = mPopup.getHeight();
+ if (!mPopup.isShowing()) {
+ // Popup isn't yet shown, get estimated size from the menu item buttons container.
+ mMenuItemButtonsContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ popupWidth = mMenuItemButtonsContainer.getMeasuredWidth();
+ popupHeight = mMenuItemButtonsContainer.getMeasuredHeight();
+ }
+ int x = mContentRect.centerX() - popupWidth / 2;
+ int y;
+ if (shouldDisplayAtTopOfContent()) {
+ y = mContentRect.top - popupHeight;
+ } else {
+ y = mContentRect.bottom;
+ }
+ mCoordinates.set(x, y);
+ }
+
+ /**
+ * Returns true if this floating toolbar's menu items have been reordered or changed.
+ */
+ private boolean hasContentChanged(List<MenuItem> menuItems) {
+ return !mShowingTitles.equals(getMenuItemTitles(menuItems));
+ }
+
+ /**
+ * Returns true if there is a significant change in width of the toolbar.
+ */
+ private boolean hasWidthChanged() {
+ int actualWidth = mPopup.getWidth();
+ int difference = Math.abs(actualWidth - mSuggestedWidth);
+ return difference > (actualWidth * 0.2);
+ }
+
+ /**
+ * Returns true if the preferred positioning of the toolbar is above the content rect.
+ */
+ private boolean shouldDisplayAtTopOfContent() {
+ return mContentRect.top - getMinimumOverflowHeight(mContext) > 0;
+ }
+
+ /**
+ * Returns the visible and enabled menu items in the specified menu.
+ * This method is recursive.
+ */
+ private List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) {
+ List<MenuItem> menuItems = new ArrayList<MenuItem>();
+ for (int i = 0; (menu != null) && (i < menu.size()); i++) {
+ MenuItem menuItem = menu.getItem(i);
+ if (menuItem.isVisible() && menuItem.isEnabled()) {
+ Menu subMenu = menuItem.getSubMenu();
+ if (subMenu != null) {
+ menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
+ } else {
+ menuItems.add(menuItem);
+ }
+ }
+ }
+ return menuItems;
+ }
+
+ private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
+ List<CharSequence> titles = new ArrayList<CharSequence>();
+ for (MenuItem menuItem : menuItems) {
+ titles.add(menuItem.getTitle());
+ }
+ return titles;
+ }
+
+ private void layoutMenuItemButtons(List<MenuItem> menuItems) {
+ final int toolbarWidth = getAdjustedToolbarWidth(mContext, mSuggestedWidth)
+ // Reserve space for the "open overflow" button.
+ - getEstimatedOpenOverflowButtonWidth(mContext);
+
+ int availableWidth = toolbarWidth;
+ LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
+
+ mMenuItemButtonsContainer.removeAllViews();
+
+ boolean isFirstItem = true;
+ while (!remainingMenuItems.isEmpty()) {
+ final MenuItem menuItem = remainingMenuItems.peek();
+ Button menuItemButton = createMenuItemButton(mContext, menuItem);
+
+ // Adding additional left padding for the first button to even out button spacing.
+ if (isFirstItem) {
+ menuItemButton.setPadding(
+ 2 * menuItemButton.getPaddingLeft(),
+ menuItemButton.getPaddingTop(),
+ menuItemButton.getPaddingRight(),
+ menuItemButton.getPaddingBottom());
+ isFirstItem = false;
+ }
+
+ // Adding additional right padding for the last button to even out button spacing.
+ if (remainingMenuItems.size() == 1) {
+ menuItemButton.setPadding(
+ menuItemButton.getPaddingLeft(),
+ menuItemButton.getPaddingTop(),
+ 2 * menuItemButton.getPaddingRight(),
+ menuItemButton.getPaddingBottom());
+ }
+
+ menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ int menuItemButtonWidth = Math.min(menuItemButton.getMeasuredWidth(), toolbarWidth);
+ if (menuItemButtonWidth <= availableWidth) {
+ menuItemButton.setTag(menuItem);
+ menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+ mMenuItemButtonsContainer.addView(menuItemButton);
+ menuItemButton.getLayoutParams().width = menuItemButtonWidth;
+ availableWidth -= menuItemButtonWidth;
+ remainingMenuItems.pop();
+ } else {
+ // The "open overflow" button launches the vertical overflow from the
+ // floating toolbar.
+ createOpenOverflowButtonIfNotExists();
+ mMenuItemButtonsContainer.addView(mOpenOverflowButton);
+ break;
+ }
+ }
+ mPopup.setContentView(mMenuItemButtonsContainer);
+ }
+
+ /**
+ * Creates and returns the button that opens the vertical overflow.
+ */
+ private void createOpenOverflowButtonIfNotExists() {
+ mOpenOverflowButton = (ImageButton) LayoutInflater.from(mContext)
+ .inflate(R.layout.floating_popup_open_overflow_button, null);
+ mOpenOverflowButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Open the overflow.
+ }
+ });
+ }
+
+ /**
+ * Creates and returns a floating toolbar menu buttons container.
+ */
+ private static ViewGroup createMenuButtonsContainer(Context context) {
+ return (ViewGroup) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_container, null);
+ }
+
+ /**
+ * Creates and returns a menu button for the specified menu item.
+ */
+ private static Button createMenuItemButton(Context context, MenuItem menuItem) {
+ Button menuItemButton = (Button) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_menu_button, null);
+ menuItemButton.setText(menuItem.getTitle());
+ menuItemButton.setContentDescription(menuItem.getTitle());
+ return menuItemButton;
+ }
+
+ private static int getMinimumOverflowHeight(Context context) {
+ return context.getResources().
+ getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height);
+ }
+
+ private static int getEstimatedOpenOverflowButtonWidth(Context context) {
+ return context.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width);
+ }
+
+ private static int getAdjustedToolbarWidth(Context context, int width) {
+ if (width <= 0 || width > getScreenWidth(context)) {
+ width = context.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_default_width);
+ }
+ return width;
+ }
+
+ /**
+ * Returns the device's screen width.
+ */
+ public static int getScreenWidth(Context context) {
+ return context.getResources().getDisplayMetrics().widthPixels;
+ }
+
+ /**
+ * Returns the device's screen height.
+ */
+ public static int getScreenHeight(Context context) {
+ return context.getResources().getDisplayMetrics().heightPixels;
+ }
+
+
+ /**
+ * A popup window used by the floating toolbar.
+ */
+ private static final class FloatingToolbarPopup {
+
+ private final View mParent;
+ private final PopupWindow mPopupWindow;
+ private final ViewGroup mContentContainer;
+ private final Animator.AnimatorListener mOnDismissEnd =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPopupWindow.dismiss();
+ mDismissAnimating = false;
+ }
+ };
+ private final AnimatorSet mGrowFadeInFromBottomAnimation;
+ private final AnimatorSet mShrinkFadeOutFromBottomAnimation;
+
+ private boolean mDismissAnimating;
+
+ /**
+ * Initializes a new floating bar popup.
+ *
+ * @param parent A parent view to get the {@link View#getWindowToken()} token from.
+ */
+ public FloatingToolbarPopup(View parent) {
+ mParent = Preconditions.checkNotNull(parent);
+ mContentContainer = createContentContainer(parent.getContext());
+ mPopupWindow = createPopupWindow(mContentContainer);
+ mGrowFadeInFromBottomAnimation = createGrowFadeInFromBottom(mContentContainer);
+ mShrinkFadeOutFromBottomAnimation =
+ createShrinkFadeOutFromBottomAnimation(mContentContainer, mOnDismissEnd);
+ }
+
+ /**
+ * Shows this popup at the specified coordinates.
+ * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+ * If this popup is already showing, this will be a no-op.
+ */
+ public void show(int x, int y) {
+ if (isShowing()) {
+ updateCoordinates(x, y);
+ return;
+ }
+
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, 0, 0);
+ positionOnScreen(x, y);
+ growFadeInFromBottom();
+
+ mDismissAnimating = false;
+ }
+
+ /**
+ * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+ */
+ public void dismiss() {
+ if (!isShowing()) {
+ return;
+ }
+
+ if (mDismissAnimating) {
+ // This window is already dismissing. Don't restart the animation.
+ return;
+ }
+ mDismissAnimating = true;
+ shrinkFadeOutFromBottom();
+ }
+
+ /**
+ * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+ */
+ public boolean isShowing() {
+ return mPopupWindow.isShowing() && !mDismissAnimating;
+ }
+
+ /**
+ * Updates the coordinates of this popup.
+ * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+ */
+ public void updateCoordinates(int x, int y) {
+ if (isShowing()) {
+ positionOnScreen(x, y);
+ }
+ }
+
+ /**
+ * Sets the content of this popup.
+ */
+ public void setContentView(View view) {
+ Preconditions.checkNotNull(view);
+ mContentContainer.removeAllViews();
+ mContentContainer.addView(view);
+ }
+
+ /**
+ * Returns the width of this popup.
+ */
+ public int getWidth() {
+ return mContentContainer.getWidth();
+ }
+
+ /**
+ * Returns the height of this popup.
+ */
+ public int getHeight() {
+ return mContentContainer.getHeight();
+ }
+
+ /**
+ * Returns the context this popup is running in.
+ */
+ public Context getContext() {
+ return mContentContainer.getContext();
+ }
+
+ private void positionOnScreen(int x, int y) {
+ if (getWidth() == 0) {
+ // content size is yet to be measured.
+ mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ }
+ x = clamp(x, 0, getScreenWidth(getContext()) - getWidth());
+ y = clamp(y, 0, getScreenHeight(getContext()) - getHeight());
+
+ // Position the view w.r.t. the window.
+ mContentContainer.setX(x);
+ mContentContainer.setY(y);
+ }
+
+ /**
+ * Performs the "grow and fade in from the bottom" animation on the floating popup.
+ */
+ private void growFadeInFromBottom() {
+ setPivot();
+ mGrowFadeInFromBottomAnimation.start();
+ }
+
+ /**
+ * Performs the "shrink and fade out from bottom" animation on the floating popup.
+ */
+ private void shrinkFadeOutFromBottom() {
+ setPivot();
+ mShrinkFadeOutFromBottomAnimation.start();
+ }
+
+ /**
+ * Sets the popup content container's pivot.
+ */
+ private void setPivot() {
+ mContentContainer.setPivotX(mContentContainer.getMeasuredWidth() / 2);
+ mContentContainer.setPivotY(mContentContainer.getMeasuredHeight());
+ }
+
+ private static ViewGroup createContentContainer(Context context) {
+ return (ViewGroup) LayoutInflater.from(context)
+ .inflate(R.layout.floating_popup_container, null);
+ }
+
+ private static PopupWindow createPopupWindow(View content) {
+ ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+ PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+ popupWindow.setAnimationStyle(0);
+ popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ popupWindow.setWidth(getScreenWidth(content.getContext()));
+ popupWindow.setHeight(getScreenHeight(content.getContext()));
+ content.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ popupContentHolder.addView(content);
+ return popupWindow;
+ }
+
+ /**
+ * Creates a "grow and fade in from the bottom" animation for the specified view.
+ *
+ * @param view The view to animate
+ */
+ private static AnimatorSet createGrowFadeInFromBottom(View view) {
+ AnimatorSet growFadeInFromBottomAnimation = new AnimatorSet();
+ growFadeInFromBottomAnimation.playTogether(
+ ObjectAnimator.ofFloat(view, View.SCALE_X, 0.5f, 1).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.5f, 1).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(75));
+ return growFadeInFromBottomAnimation;
+ }
+
+ /**
+ * Creates a "shrink and fade out from bottom" animation for the specified view.
+ *
+ * @param view The view to animate
+ * @param listener The animation listener
+ */
+ private static AnimatorSet createShrinkFadeOutFromBottomAnimation(
+ View view, Animator.AnimatorListener listener) {
+ AnimatorSet shrinkFadeOutFromBottomAnimation = new AnimatorSet();
+ shrinkFadeOutFromBottomAnimation.playTogether(
+ ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125),
+ ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75));
+ shrinkFadeOutFromBottomAnimation.setStartDelay(150);
+ shrinkFadeOutFromBottomAnimation.addListener(listener);
+ return shrinkFadeOutFromBottomAnimation;
+ }
+
+ /**
+ * Returns value, restricted to the range min->max (inclusive).
+ * If maximum is less than minimum, the result is undefined.
+ *
+ * @param value The value to clamp.
+ * @param minimum The minimum value in the range.
+ * @param maximum The maximum value in the range. Must not be less than minimum.
+ */
+ private static int clamp(int value, int minimum, int maximum) {
+ return Math.max(minimum, Math.min(value, maximum));
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 52bbabf..8be34e7 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -269,9 +269,9 @@ public class LockPatternView extends View {
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
- mRegularColor = getResources().getColor(R.color.lock_pattern_view_regular_color);
- mErrorColor = getResources().getColor(R.color.lock_pattern_view_error_color);
- mSuccessColor = getResources().getColor(R.color.lock_pattern_view_success_color);
+ mRegularColor = context.getColor(R.color.lock_pattern_view_regular_color);
+ mErrorColor = context.getColor(R.color.lock_pattern_view_error_color);
+ mSuccessColor = context.getColor(R.color.lock_pattern_view_success_color);
mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, mRegularColor);
mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, mErrorColor);
mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, mSuccessColor);
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index f916e6f..8018942 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -137,7 +137,7 @@ public class ViewPager extends ViewGroup {
private int mRestoredCurItem = -1;
private Parcelable mRestoredAdapterState = null;
private ClassLoader mRestoredClassLoader = null;
- private Scroller mScroller;
+ private final Scroller mScroller;
private PagerObserver mObserver;
private int mPageMargin;
@@ -162,9 +162,9 @@ public class ViewPager extends ViewGroup {
private boolean mIsBeingDragged;
private boolean mIsUnableToDrag;
- private int mDefaultGutterSize;
+ private final int mDefaultGutterSize;
private int mGutterSize;
- private int mTouchSlop;
+ private final int mTouchSlop;
/**
* Position of the last motion event.
*/
@@ -187,10 +187,10 @@ public class ViewPager extends ViewGroup {
* Determines speed during touch scrolling
*/
private VelocityTracker mVelocityTracker;
- private int mMinimumVelocity;
- private int mMaximumVelocity;
- private int mFlingDistance;
- private int mCloseEnough;
+ private final int mMinimumVelocity;
+ private final int mMaximumVelocity;
+ private final int mFlingDistance;
+ private final int mCloseEnough;
// If the pager is at least this close to its final position, complete the scroll
// on touch down and let the user interact with the content inside instead of
@@ -200,8 +200,8 @@ public class ViewPager extends ViewGroup {
private boolean mFakeDragging;
private long mFakeDragBeginTime;
- private EdgeEffect mLeftEdge;
- private EdgeEffect mRightEdge;
+ private final EdgeEffect mLeftEdge;
+ private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
private boolean mNeedCalculatePageOffsets = false;
@@ -339,20 +339,24 @@ public class ViewPager extends ViewGroup {
interface Decor {}
public ViewPager(Context context) {
- super(context);
- initViewPager();
+ this(context, null);
}
public ViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- initViewPager();
+ this(context, attrs, 0);
}
- void initViewPager() {
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
- final Context context = getContext();
+
mScroller = new Scroller(context, sInterpolator);
final ViewConfiguration configuration = ViewConfiguration.get(context);
final float density = context.getResources().getDisplayMetrics().density;