diff options
Diffstat (limited to 'core/java/android')
52 files changed, 2674 insertions, 1286 deletions
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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 6dde07b..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 */ @@ -346,7 +346,7 @@ public class DevicePolicyManager { * {@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} 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_ADMIN_MINIMUM_VERSION_CODE @@ -363,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. @@ -392,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"; @@ -416,7 +418,7 @@ 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 @@ -428,7 +430,7 @@ public class DevicePolicyManager { * {@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} 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_MINIMUM_VERSION_CODE @@ -438,19 +440,20 @@ public class DevicePolicyManager { * 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 @@ -460,7 +463,7 @@ public class DevicePolicyManager { * A String extra holding the MAC address of the Bluetooth device to connect to with status * updates during 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_BT_MAC_ADDRESS @@ -472,7 +475,7 @@ public class DevicePolicyManager { * * <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} 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_BT_UUID @@ -484,7 +487,7 @@ public class DevicePolicyManager { * * <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} 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_BT_DEVICE_ID @@ -494,31 +497,26 @@ public class DevicePolicyManager { * 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} 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_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY"; - /** - * This MIME type is used for starting the Device Owner 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> A typical use case would be a device that is owned by a company, but used by either an - * employee or client. + * 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> @@ -529,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 @@ -551,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. @@ -577,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 @@ -593,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 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/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 39a70be..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 */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9be96a1..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). diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 8f17845..2496e45 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -340,8 +340,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * 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. - * - * @hide */ public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27; @@ -379,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; @@ -655,7 +654,7 @@ 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); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e5859d0..212cf6d 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -376,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. * @@ -394,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()); @@ -574,7 +564,7 @@ public class PackageParser { for (int i=0; i<N; i++) { final String perm = p.requestedPermissions.get(i); pi.requestedPermissions[i] = perm; - // The notion of requried permissions is deprecated but for compatibility. + // 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; diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 95ad57e..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<>(); } /** @@ -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/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/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/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/NetworkStats.java b/core/java/android/net/NetworkStats.java index 0766253..77d7e0c 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -50,12 +50,19 @@ public class NetworkStats implements Parcelable { public static final int UID_ALL = -1; /** {@link #tag} value matching any tag. */ public static final int TAG_ALL = -1; - /** {@link #set} value when all sets combined. */ + /** {@link #set} value when all sets combined, not including debug sets. */ public static final int SET_ALL = -1; /** {@link #set} value where background data is accounted. */ public static final int SET_DEFAULT = 0; /** {@link #set} value where foreground data is accounted. */ public static final int SET_FOREGROUND = 1; + /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */ + public static final int SET_DEBUG_START = 1000; + /** Debug {@link #set} value when the VPN stats are moved in. */ + public static final int SET_DBG_VPN_IN = 1001; + /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */ + public static final int SET_DBG_VPN_OUT = 1002; + /** {@link #tag} value for total data across all tags. */ public static final int TAG_NONE = 0; @@ -729,6 +736,10 @@ public class NetworkStats implements Parcelable { return "DEFAULT"; case SET_FOREGROUND: return "FOREGROUND"; + case SET_DBG_VPN_IN: + return "DBG_VPN_IN"; + case SET_DBG_VPN_OUT: + return "DBG_VPN_OUT"; default: return "UNKNOWN"; } @@ -745,12 +756,27 @@ public class NetworkStats implements Parcelable { return "def"; case SET_FOREGROUND: return "fg"; + case SET_DBG_VPN_IN: + return "vpnin"; + case SET_DBG_VPN_OUT: + return "vpnout"; default: return "unk"; } } /** + * @return true if the querySet matches the dataSet. + */ + public static boolean setMatches(int querySet, int dataSet) { + if (querySet == dataSet) { + return true; + } + // SET_ALL matches all non-debugging sets. + return querySet == SET_ALL && dataSet < SET_DEBUG_START; + } + + /** * Return text description of {@link #tag} value. */ public static String tagToString(int tag) { @@ -843,6 +869,9 @@ public class NetworkStats implements Parcelable { if (recycle.uid == UID_ALL) { throw new IllegalStateException( "Cannot adjust VPN accounting on an iface aggregated NetworkStats."); + } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { + throw new IllegalStateException( + "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*"); } if (recycle.uid == tunUid && recycle.tag == TAG_NONE @@ -906,6 +935,9 @@ public class NetworkStats implements Parcelable { combineValues(tmpEntry); if (tag[i] == TAG_NONE) { moved.add(tmpEntry); + // Add debug info + tmpEntry.set = SET_DBG_VPN_IN; + combineValues(tmpEntry); } } } @@ -913,6 +945,13 @@ public class NetworkStats implements Parcelable { } private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) { + // Add debug info + moved.uid = tunUid; + moved.set = SET_DBG_VPN_OUT; + moved.tag = TAG_NONE; + moved.iface = underlyingIface; + combineValues(moved); + // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than // the TAG_NONE traffic. int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 26e6b85..8b3ecae 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -29,6 +29,7 @@ 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.SparseArray; import android.util.SparseIntArray; @@ -283,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. @@ -323,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} @@ -501,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. @@ -1352,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); @@ -2382,12 +2382,12 @@ public abstract class BatteryStats implements Parcelable { 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, @@ -2402,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, @@ -2544,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); @@ -2553,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 @@ -2566,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()), @@ -2577,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) { @@ -2629,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 @@ -2675,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); @@ -2771,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); } } } @@ -2863,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); @@ -2992,7 +2985,7 @@ public abstract class BatteryStats implements Parcelable { sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); 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); @@ -3002,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); @@ -3242,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); @@ -3367,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(); @@ -3382,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); @@ -3439,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()); @@ -3476,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)); } @@ -3490,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); @@ -3526,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))); } @@ -3557,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(" "); @@ -3565,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) { @@ -3636,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); @@ -3655,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 "); @@ -3785,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); @@ -3808,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); @@ -3828,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); @@ -3862,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"); } - uidActivity = true; } + 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; } } - 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)"); @@ -4498,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; @@ -4647,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(); @@ -4691,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; } @@ -4769,7 +4754,7 @@ public abstract class BatteryStats implements Parcelable { LevelStepTracker csteps = getDailyChargeLevelStepTracker(); ArrayList<PackageChange> pkgc = getDailyPackageChanges(); if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) { - if ((flags&DUMP_DAILY_ONLY) != 0) { + if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) { if (dumpDurationSteps(pw, " ", " Current daily discharge step durations:", dsteps, false)) { dumpDailyLevelStepSummary(pw, " ", "Discharge", dsteps, @@ -4801,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, @@ -4830,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") @@ -4848,7 +4828,7 @@ public abstract class BatteryStats implements Parcelable { 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()) { @@ -4874,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; } @@ -4924,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 418641f..804d3d0 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -51,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 81745b3..01c9a21 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -712,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. @@ -917,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/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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 74b0a1c..e4a6f07 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -4809,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 = 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 index c7274e8..0b3bf45 100644 --- a/core/java/android/security/NetworkSecurityPolicy.java +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -19,48 +19,57 @@ package android.security; /** * Network security policy. * - * @hide + * <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 boolean mCleartextTrafficPermitted = true; + private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy(); - private NetworkSecurityPolicy() {} + private NetworkSecurityPolicy() {} - /** - * Gets the policy. - */ - public static NetworkSecurityPolicy getInstance() { - return INSTANCE; - } + /** + * 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; + } - /** - * Checks whether cleartext network traffic (e.g., HTTP, 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 - * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use - * cleartext traffic. Third-party libraries are encouraged to honor this setting as well. - */ - public boolean isCleartextTrafficPermitted() { - synchronized (this) { - return mCleartextTrafficPermitted; + /** + * 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) { - synchronized (this) { - mCleartextTrafficPermitted = permitted; + /** + * 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/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/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index d46b6f5..1674950 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -155,7 +155,6 @@ public abstract class WallpaperService extends Service { WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; int mCurWindowFlags = mWindowFlags; int mCurWindowPrivateFlags = mWindowPrivateFlags; - int mOutsetBottomPx; final Rect mVisibleInsets = new Rect(); final Rect mWinFrame = new Rect(); final Rect mOverscanInsets = new Rect(); @@ -624,18 +623,9 @@ 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) { - mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx( - getResources().getDisplayMetrics(), windowStyle); - } mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources()); windowStyle.recycle(); @@ -770,10 +760,7 @@ public abstract class WallpaperService extends Service { mDispatchedStableInsets.set(mStableInsets); mFinalSystemInsets.set(mDispatchedOverscanInsets); mFinalStableInsets.set(mDispatchedStableInsets); - if (mOutsetBottomPx != 0) { - mFinalSystemInsets.bottom = - mIWallpaperEngine.mDisplayPadding.bottom + mOutsetBottomPx; - } + mFinalSystemInsets.bottom = mIWallpaperEngine.mDisplayPadding.bottom; WindowInsets insets = new WindowInsets(mFinalSystemInsets, null, mFinalStableInsets, mWindowIsRound); onApplyWindowInsets(insets); diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 8cf1b4b..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; } } 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/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/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/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/View.java b/core/java/android/view/View.java index f5de8e3..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 @@ -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); } /** @@ -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/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/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/widget/Editor.java b/core/java/android/widget/Editor.java index 87fcd81..6e24837 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -4664,8 +4664,8 @@ public class Editor { // Otherwise the user inserted the composition. String newText = TextUtils.substring(source, start, end); - EditOperation edit = new EditOperation(mEditor, false, "", dstart, newText); - recordEdit(edit); + EditOperation edit = new EditOperation(mEditor, "", dstart, newText); + recordEdit(edit, false /* forceMerge */); return true; } @@ -4684,11 +4684,15 @@ public class Editor { // Build a new operation with all the information from this edit. String newText = TextUtils.substring(source, start, end); String oldText = TextUtils.substring(dest, dstart, dend); - EditOperation edit = new EditOperation(mEditor, forceMerge, oldText, dstart, newText); - recordEdit(edit); + EditOperation edit = new EditOperation(mEditor, oldText, dstart, newText); + recordEdit(edit, forceMerge); } - private void recordEdit(EditOperation edit) { + /** + * 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"); @@ -4698,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. @@ -4773,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; @@ -4784,13 +4792,10 @@ public class Editor { /** * Constructs an edit operation from a text input operation on editor that replaces the - * oldText starting at dstart with newText. If forceMerge is true then always forcibly - * merge this operation with any previous one. + * oldText starting at dstart with newText. */ - public EditOperation(Editor editor, boolean forceMerge, String oldText, int dstart, - String newText) { + public EditOperation(Editor editor, String oldText, int dstart, String newText) { super(editor.mUndoOwner); - mForceMerge = forceMerge; mOldText = oldText; mNewText = newText; @@ -4817,7 +4822,6 @@ public class Editor { 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(); @@ -4829,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); @@ -4881,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); @@ -4942,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/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; |
