diff options
63 files changed, 2003 insertions, 460 deletions
@@ -126,6 +126,7 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ core/java/android/hardware/location/IGeofenceHardware.aidl \ core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \ + core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \ core/java/android/hardware/usb/IUsbManager.aidl \ core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index 082a395..ce3a0f0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5613,7 +5613,6 @@ package android.content { method public abstract java.lang.String[] fileList(); method public abstract android.content.Context getApplicationContext(); method public abstract android.content.pm.ApplicationInfo getApplicationInfo(); - method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions(); method public abstract android.content.res.AssetManager getAssets(); method public abstract java.io.File getCacheDir(); method public abstract java.lang.ClassLoader getClassLoader(); @@ -6200,8 +6199,9 @@ package android.content { field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER"; field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token"; field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING"; - field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions"; + field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle"; field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; + field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; @@ -10471,13 +10471,14 @@ package android.hardware.input { package android.hardware.location { public final class GeofenceHardware { - method public boolean addCircularFence(int, double, double, double, int, int, int, int, int, android.hardware.location.GeofenceHardwareCallback); - method public int[] getMonitoringTypesAndStatus(); + method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback); + method public int[] getMonitoringTypes(); + method public int getStatusOfMonitoringType(int); method public boolean pauseGeofence(int, int); - method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback); + method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback); method public boolean removeGeofence(int, int); method public boolean resumeGeofence(int, int, int); - method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback); + method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback); field public static final int GEOFENCE_ENTERED = 1; // 0x1 field public static final int GEOFENCE_ERROR_ID_EXISTS = 2; // 0x2 field public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3; // 0x3 @@ -10496,13 +10497,33 @@ package android.hardware.location { public abstract class GeofenceHardwareCallback { ctor public GeofenceHardwareCallback(); method public void onGeofenceAdd(int, int); - method public void onGeofenceChange(int, int, android.location.Location, long, int); method public void onGeofencePause(int, int); method public void onGeofenceRemove(int, int); method public void onGeofenceResume(int, int); + method public void onGeofenceTransition(int, int, android.location.Location, long, int); + } + + public abstract class GeofenceHardwareMonitorCallback { + ctor public GeofenceHardwareMonitorCallback(); method public void onMonitoringSystemChange(int, boolean, android.location.Location); } + public final class GeofenceHardwareRequest { + ctor public GeofenceHardwareRequest(); + method public static android.hardware.location.GeofenceHardwareRequest createCircularGeofence(double, double, double); + method public int getLastTransition(); + method public double getLatitude(); + method public double getLongitude(); + method public int getMonitorTransitions(); + method public int getNotificationResponsiveness(); + method public double getRadius(); + method public int getUnknownTimer(); + method public void setLastTransition(int); + method public void setMonitorTransitions(int); + method public void setNotificationResponsiveness(int); + method public void setUnknownTimer(int); + } + } package android.hardware.usb { @@ -11796,6 +11817,66 @@ package android.media { ctor public MediaCryptoException(java.lang.String); } + public final class MediaDrm { + ctor public MediaDrm(java.util.UUID) throws android.media.MediaDrmException; + method public void closeSession(byte[]); + method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String); + method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>); + method public byte[] getPropertyByteArray(java.lang.String); + method public java.lang.String getPropertyString(java.lang.String); + method public android.media.MediaDrm.ProvisionRequest getProvisionRequest(); + method public java.util.List<byte[]> getSecureStops(); + method public static final boolean isCryptoSchemeSupported(java.util.UUID); + method public byte[] openSession(); + method public byte[] provideKeyResponse(byte[], byte[]); + method public void provideProvisionResponse(byte[]); + method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]); + method public final void release(); + method public void releaseSecureStops(byte[]); + method public void removeKeys(byte[]); + method public void restoreKeys(byte[], byte[]); + method public void setOnEventListener(android.media.MediaDrm.OnEventListener); + method public void setPropertyByteArray(java.lang.String, byte[]); + method public void setPropertyString(java.lang.String, java.lang.String); + field public static final int EVENT_KEY_EXPIRED = 3; // 0x3 + field public static final int EVENT_KEY_REQUIRED = 2; // 0x2 + field public static final int EVENT_PROVISION_REQUIRED = 1; // 0x1 + field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4 + field public static final int KEY_TYPE_OFFLINE = 2; // 0x2 + field public static final int KEY_TYPE_RELEASE = 3; // 0x3 + field public static final int KEY_TYPE_STREAMING = 1; // 0x1 + field public static final java.lang.String PROPERTY_ALGORITHM = "algorithm"; + field public static final java.lang.String PROPERTY_DESCRIPTION = "description"; + field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; + field public static final java.lang.String PROPERTY_VENDOR = "vendor"; + field public static final java.lang.String PROPERTY_VERSION = "version"; + } + + public final class MediaDrm.CryptoSession { + method public byte[] decrypt(byte[], byte[], byte[]); + method public byte[] encrypt(byte[], byte[], byte[]); + method public byte[] sign(byte[], byte[]); + method public boolean verify(byte[], byte[], byte[]); + } + + public static final class MediaDrm.KeyRequest { + method public byte[] getData(); + method public java.lang.String getDefaultUrl(); + } + + public static abstract interface MediaDrm.OnEventListener { + method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]); + } + + public static final class MediaDrm.ProvisionRequest { + method public byte[] getData(); + method public java.lang.String getDefaultUrl(); + } + + public final class MediaDrmException extends java.lang.Exception { + ctor public MediaDrmException(java.lang.String); + } + public final class MediaExtractor { ctor public MediaExtractor(); method public boolean advance(); @@ -17551,7 +17632,7 @@ package android.os { } public class UserManager { - method public static synchronized android.os.UserManager get(android.content.Context); + method public android.os.Bundle getApplicationRestrictions(java.lang.String); method public long getSerialNumberForUser(android.os.UserHandle); method public int getUserCount(); method public android.os.UserHandle getUserForSerialNumber(long); @@ -30044,7 +30125,6 @@ package android.widget { method public void setRelativeScrollPosition(int, int); method public deprecated void setRemoteAdapter(int, int, android.content.Intent); method public void setRemoteAdapter(int, android.content.Intent); - method public void setRemoteAdapter(int, java.util.ArrayList<android.widget.RemoteViews>, int); method public void setScrollPosition(int, int); method public void setShort(int, java.lang.String, short); method public void setString(int, java.lang.String, java.lang.String); diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 7b07438..0d7f0a7 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -134,11 +134,6 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } - public List<RestrictionEntry> getApplicationRestrictions() { - return ((UserManager) getSystemService(USER_SERVICE)) - .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle()); - } - public void registerComponentCallbacks(ComponentCallbacks callback) { synchronized (mComponentCallbacks) { mComponentCallbacks.add(callback); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 03e241a..5bd28b9 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -293,15 +293,6 @@ public abstract class Context { public abstract Context getApplicationContext(); /** - * Returns the list of restrictions for the application, or null if there are no - * restrictions. - * @return - */ - public List<RestrictionEntry> getApplicationRestrictions() { - return getApplicationContext().getApplicationRestrictions(); - } - - /** * Add a new {@link ComponentCallbacks} to the base application of the * Context, which will be called at the same times as the ComponentCallbacks * methods of activities and other components are called. Note that you diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 97ad7dd..1ab1eb8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2417,11 +2417,16 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast to a specific application to query any supported restrictions to impose - * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS}, + * on restricted users. The broadcast intent contains an extra + * {@link #EXTRA_RESTRICTIONS_BUNDLE} with the currently persisted + * restrictions as a Bundle of key/value pairs. The value types can be Boolean, String or + * String[] depending on the restriction type.<p/> + * The response should contain an extra {@link #EXTRA_RESTRICTIONS_LIST}, * which is of type <code>ArrayList<RestrictionEntry></code>. It can also * contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>. * The activity specified by that intent will be launched for a result which must contain - * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted. + * the extra {@link #EXTRA_RESTRICTIONS_LIST}. The keys and values of the returned restrictions + * will be persisted. * @see RestrictionEntry */ public static final String ACTION_GET_RESTRICTION_ENTRIES = @@ -3160,7 +3165,8 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.ALLOW_MULTIPLE"; /** - * The userHandle carried with broadcast intents related to addition, removal and switching of users + * The userHandle carried with broadcast intents related to addition, removal and switching of + * users * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}. * @hide */ @@ -3169,9 +3175,18 @@ public class Intent implements Parcelable, Cloneable { /** * Extra used in the response from a BroadcastReceiver that handles - * {@link #ACTION_GET_RESTRICTION_ENTRIES}. + * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is + * <code>ArrayList<RestrictionEntry></code>. + */ + public static final String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; + + /** + * Extra sent in the intent to the BroadcastReceiver that handles + * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is a Bundle containing + * the restrictions as key/value pairs. */ - public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions"; + public static final String EXTRA_RESTRICTIONS_BUNDLE = + "android.intent.extra.restrictions_bundle"; /** * Extra used in the response from a BroadcastReceiver that handles diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java index 35bbb9c..e67d0d7 100644 --- a/core/java/android/hardware/location/GeofenceHardware.java +++ b/core/java/android/hardware/location/GeofenceHardware.java @@ -129,6 +129,9 @@ public final class GeofenceHardware { private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper> mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>(); + private HashMap<GeofenceHardwareMonitorCallback, GeofenceHardwareMonitorCallbackWrapper> + mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback, + GeofenceHardwareMonitorCallbackWrapper>(); /** * @hide */ @@ -137,8 +140,29 @@ public final class GeofenceHardware { } /** - * Returns all the hardware geofence monitoring systems and their status. - * Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE}, + * Returns all the hardware geofence monitoring systems which are supported + * + * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state + * of a monitoring system. + * + * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access + * geofencing in hardware. + * + * @return An array of all the monitoring types. + * An array of length 0 is returned in case of errors. + */ + public int[] getMonitoringTypes() { + try { + return mService.getMonitoringTypes(); + } catch (RemoteException e) { + } + return new int[0]; + } + + /** + * Returns current status of a hardware geofence monitoring system. + * + * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE}, * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED} * * <p> Some supported hardware monitoring systems might not be available @@ -147,18 +171,15 @@ public final class GeofenceHardware { * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to * {@link #MONITOR_CURRENTLY_UNAVAILABLE}. * - * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access - * geofencing in hardware. - * - * @return An array indexed by the various monitoring types and their status. - * An array of length 0 is returned in case of errors. + * @param monitoringType + * @return Current status of the monitoring type. */ - public int[] getMonitoringTypesAndStatus() { + public int getStatusOfMonitoringType(int monitoringType) { try { - return mService.getMonitoringTypesAndStatus(); + return mService.getStatusOfMonitoringType(monitoringType); } catch (RemoteException e) { + return MONITOR_UNSUPPORTED; } - return new int[0]; } /** @@ -167,8 +188,10 @@ public final class GeofenceHardware { * <p> When the device detects that is has entered, exited or is uncertain * about the area specified by the geofence, the given callback will be called. * - * <p> The {@link GeofenceHardwareCallback#onGeofenceChange} callback will be called, - * with the following parameters + * <p> If this call returns true, it means that the geofence has been sent to the hardware. + * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the + * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be + * called with the following parameters when a transition event occurs. * <ul> * <li> The geofence Id * <li> The location object indicating the last known location. @@ -195,43 +218,46 @@ public final class GeofenceHardware { * which abstracts the hardware should be used instead. All the checks are done by the higher * level public API. Any needed locking should be handled by the higher level API. * - * @param latitude Latitude of the area to be monitored. - * @param longitude Longitude of the area to be monitored. - * @param radius Radius (in meters) of the area to be monitored. - * @param lastTransition The current state of the geofence. Can be one of - * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, - * {@link #GEOFENCE_UNCERTAIN}. - * @param monitorTransitions Bitwise OR of {@link #GEOFENCE_ENTERED}, - * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN} - * @param notificationResponsivenes Defines the best-effort description - * of how soon should the callback be called when the transition - * associated with the Geofence is triggered. For instance, if - * set to 1000 millseconds with {@link #GEOFENCE_ENTERED}, - * the callback will be called 1000 milliseconds within entering - * the geofence. This parameter is defined in milliseconds. - * @param unknownTimer The time limit after which the - * {@link #GEOFENCE_UNCERTAIN} transition - * should be triggered. This paramter is defined in milliseconds. + * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to + * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object + * in this call. + * + * @param geofenceId The id associated with the geofence. * @param monitoringType The type of the hardware subsystem that should be used * to monitor the geofence. + * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the + * geofence. * @param callback {@link GeofenceHardwareCallback} that will be use to notify the * transition. - * @return true on success. + * @return true when the geofence is successfully sent to the hardware for addition. + * @throws IllegalArgumentException when the geofence request type is not supported. */ - public boolean addCircularFence(int geofenceId, double latitude, double longitude, - double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes, - int unknownTimer, int monitoringType, GeofenceHardwareCallback callback) { + public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest + geofenceRequest, GeofenceHardwareCallback callback) { try { - return mService.addCircularFence(geofenceId, latitude, longitude, radius, - lastTransition, monitorTransitions, notificationResponsivenes, unknownTimer, - monitoringType, getCallbackWrapper(callback)); + if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) { + return mService.addCircularFence(geofenceId, monitoringType, + geofenceRequest.getLatitude(), + geofenceRequest.getLongitude(), geofenceRequest.getRadius(), + geofenceRequest.getLastTransition(), + geofenceRequest.getMonitorTransitions(), + geofenceRequest.getNotificationResponsiveness(), + geofenceRequest.getUnknownTimer(), + getCallbackWrapper(callback)); + } else { + throw new IllegalArgumentException("Geofence Request type not supported"); + } } catch (RemoteException e) { } return false; } /** - * Removes a geofence added by {@link #addCircularFence} call. + * Removes a geofence added by {@link #addGeofence} call. + * + * <p> If this call returns true, it means that the geofence has been sent to the hardware. + * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the + * remove call from the hardware. * * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. @@ -246,7 +272,7 @@ public final class GeofenceHardware { * @param geofenceId The id of the geofence. * @param monitoringType The type of the hardware subsystem that should be used * to monitor the geofence. - * @return true on success. + * @return true when the geofence is successfully sent to the hardware for removal. . */ public boolean removeGeofence(int geofenceId, int monitoringType) { try { @@ -257,7 +283,11 @@ public final class GeofenceHardware { } /** - * Pauses the monitoring of a geofence added by {@link #addCircularFence} call. + * Pauses the monitoring of a geofence added by {@link #addGeofence} call. + * + * <p> If this call returns true, it means that the geofence has been sent to the hardware. + * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the + * pause call from the hardware. * * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. @@ -272,7 +302,7 @@ public final class GeofenceHardware { * @param geofenceId The id of the geofence. * @param monitoringType The type of the hardware subsystem that should be used * to monitor the geofence. - * @return true on success. + * @return true when the geofence is successfully sent to the hardware for pausing. */ public boolean pauseGeofence(int geofenceId, int monitoringType) { try { @@ -285,6 +315,10 @@ public final class GeofenceHardware { /** * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call. * + * <p> If this call returns true, it means that the geofence has been sent to the hardware. + * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the + * resume call from the hardware. + * * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. * @@ -296,15 +330,15 @@ public final class GeofenceHardware { * level public API. Any needed locking should be handled by the higher level API. * * @param geofenceId The id of the geofence. - * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED}, - * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN} * @param monitoringType The type of the hardware subsystem that should be used * to monitor the geofence. - * @return true on success. + * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED}, + * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN} + * @return true when the geofence is successfully sent to the hardware for resumption. */ - public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) { + public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) { try { - return mService.resumeGeofence(geofenceId, monitorTransition, monitoringType); + return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition); } catch (RemoteException e) { } return false; @@ -333,10 +367,10 @@ public final class GeofenceHardware { * @return true on success */ public boolean registerForMonitorStateChangeCallback(int monitoringType, - GeofenceHardwareCallback callback) { + GeofenceHardwareMonitorCallback callback) { try { return mService.registerForMonitorStateChangeCallback(monitoringType, - getCallbackWrapper(callback)); + getMonitorCallbackWrapper(callback)); } catch (RemoteException e) { } return false; @@ -361,12 +395,12 @@ public final class GeofenceHardware { * @return true on success */ public boolean unregisterForMonitorStateChangeCallback(int monitoringType, - GeofenceHardwareCallback callback) { + GeofenceHardwareMonitorCallback callback) { boolean result = false; try { result = mService.unregisterForMonitorStateChangeCallback(monitoringType, - getCallbackWrapper(callback)); - if (result) removeCallback(callback); + getMonitorCallbackWrapper(callback)); + if (result) removeMonitorCallback(callback); } catch (RemoteException e) { } @@ -391,24 +425,50 @@ public final class GeofenceHardware { } } - class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub { - private WeakReference<GeofenceHardwareCallback> mCallback; + private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) { + synchronized (mMonitorCallbacks) { + mMonitorCallbacks.remove(callback); + } + } - GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) { - mCallback = new WeakReference<GeofenceHardwareCallback>(c); + private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper( + GeofenceHardwareMonitorCallback callback) { + synchronized (mMonitorCallbacks) { + GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback); + if (wrapper == null) { + wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback); + mMonitorCallbacks.put(callback, wrapper); + } + return wrapper; + } + } + + class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub { + private WeakReference<GeofenceHardwareMonitorCallback> mCallback; + + GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) { + mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c); } public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) { - GeofenceHardwareCallback c = mCallback.get(); + GeofenceHardwareMonitorCallback c = mCallback.get(); if (c != null) c.onMonitoringSystemChange(monitoringType, available, location); } + } + + class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub { + private WeakReference<GeofenceHardwareCallback> mCallback; - public void onGeofenceChange(int geofenceId, int transition, Location location, + GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) { + mCallback = new WeakReference<GeofenceHardwareCallback>(c); + } + + public void onGeofenceTransition(int geofenceId, int transition, Location location, long timestamp, int monitoringType) { GeofenceHardwareCallback c = mCallback.get(); if (c != null) { - c.onGeofenceChange(geofenceId, transition, location, timestamp, + c.onGeofenceTransition(geofenceId, transition, location, timestamp, monitoringType); } } @@ -428,7 +488,9 @@ public final class GeofenceHardware { public void onGeofencePause(int geofenceId, int status) { GeofenceHardwareCallback c = mCallback.get(); - if (c != null) c.onGeofencePause(geofenceId, status); + if (c != null) { + c.onGeofencePause(geofenceId, status); + } } public void onGeofenceResume(int geofenceId, int status) { diff --git a/core/java/android/hardware/location/GeofenceHardwareCallback.java b/core/java/android/hardware/location/GeofenceHardwareCallback.java index 8ab582a..6cad3da 100644 --- a/core/java/android/hardware/location/GeofenceHardwareCallback.java +++ b/core/java/android/hardware/location/GeofenceHardwareCallback.java @@ -22,19 +22,6 @@ import android.location.Location; * The callback class associated with the APIs in {@link GeofenceHardware} */ public abstract class GeofenceHardwareCallback { - - /** - * The callback called when the state of a monitoring system changes. - * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a - * monitoring system. - * - * @param monitoringType The type of the monitoring system. - * @param available Indicates whether the system is currently available or not. - * @param location The last known location according to the monitoring system. - */ - public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) { - } - /** * The callback called when there is a transition to report for the specific * geofence. @@ -47,7 +34,7 @@ public abstract class GeofenceHardwareCallback { * detected * @param monitoringType Type of the monitoring system. */ - public void onGeofenceChange(int geofenceId, int transition, Location location, + public void onGeofenceTransition(int geofenceId, int transition, Location location, long timestamp, int monitoringType) { } diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java index 21f1ea6..a62b660 100644 --- a/core/java/android/hardware/location/GeofenceHardwareImpl.java +++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java @@ -21,8 +21,10 @@ import android.content.pm.PackageManager; import android.location.IGpsGeofenceHardware; import android.location.Location; import android.location.LocationManager; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; @@ -48,8 +50,9 @@ public final class GeofenceHardwareImpl { private PowerManager.WakeLock mWakeLock; private SparseArray<IGeofenceHardwareCallback> mGeofences = new SparseArray<IGeofenceHardwareCallback>(); - private ArrayList<IGeofenceHardwareCallback>[] mCallbacks = + private ArrayList<IGeofenceHardwareMonitorCallback>[] mCallbacks = new ArrayList[GeofenceHardware.NUM_MONITORS]; + private ArrayList<Reaper> mReapers = new ArrayList<Reaper>(); private IGpsGeofenceHardware mGpsService; @@ -63,11 +66,18 @@ public final class GeofenceHardwareImpl { private static final int RESUME_GEOFENCE_CALLBACK = 5; private static final int ADD_GEOFENCE = 6; private static final int REMOVE_GEOFENCE = 7; + private static final int GEOFENCE_CALLBACK_BINDER_DIED = 8; // mCallbacksHandler message types private static final int GPS_GEOFENCE_STATUS = 1; private static final int CALLBACK_ADD = 2; private static final int CALLBACK_REMOVE = 3; + private static final int MONITOR_CALLBACK_BINDER_DIED = 4; + + // mReaperHandler message types + private static final int REAPER_GEOFENCE_ADDED = 1; + private static final int REAPER_MONITOR_CALLBACK_ADDED = 2; + private static final int REAPER_REMOVED = 3; // The following constants need to match GpsLocationFlags enum in gps.h private static final int LOCATION_INVALID = 0; @@ -151,15 +161,28 @@ public final class GeofenceHardwareImpl { } } - public int[] getMonitoringTypesAndStatus() { + public int[] getMonitoringTypes() { synchronized (mSupportedMonitorTypes) { - return mSupportedMonitorTypes; + if (mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE] != + GeofenceHardware.MONITOR_UNSUPPORTED) { + return new int[] {GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE}; + } + return new int[0]; + } + } + + public int getStatusOfMonitoringType(int monitoringType) { + synchronized (mSupportedMonitorTypes) { + if (monitoringType >= mSupportedMonitorTypes.length || monitoringType < 0) { + throw new IllegalArgumentException("Unknown monitoring type"); + } + return mSupportedMonitorTypes[monitoringType]; } } - public boolean addCircularFence(int geofenceId, double latitude, double longitude, - double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes, - int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) { + public boolean addCircularFence(int geofenceId, int monitoringType, double latitude, + double longitude, double radius, int lastTransition,int monitorTransitions, + int notificationResponsivenes, int unknownTimer, IGeofenceHardwareCallback callback) { // This API is not thread safe. Operations on the same geofence need to be serialized // by upper layers if (DEBUG) { @@ -190,7 +213,11 @@ public final class GeofenceHardwareImpl { default: result = false; } - if (!result) { + if (result) { + m = mReaperHandler.obtainMessage(REAPER_GEOFENCE_ADDED, callback); + m.arg1 = monitoringType; + mReaperHandler.sendMessage(m); + } else { m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE); m.arg1 = geofenceId; mGeofenceHandler.sendMessage(m); @@ -245,7 +272,7 @@ public final class GeofenceHardwareImpl { } - public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) { + public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) { // This API is not thread safe. Operations on the same geofence need to be serialized // by upper layers if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId); @@ -268,7 +295,12 @@ public final class GeofenceHardwareImpl { } public boolean registerForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback) { + IGeofenceHardwareMonitorCallback callback) { + Message reaperMessage = + mReaperHandler.obtainMessage(REAPER_MONITOR_CALLBACK_ADDED, callback); + reaperMessage.arg1 = monitoringType; + mReaperHandler.sendMessage(reaperMessage); + Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback); m.arg1 = monitoringType; mCallbacksHandler.sendMessage(m); @@ -276,7 +308,7 @@ public final class GeofenceHardwareImpl { } public boolean unregisterForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback) { + IGeofenceHardwareMonitorCallback callback) { Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback); m.arg1 = monitoringType; mCallbacksHandler.sendMessage(m); @@ -477,13 +509,25 @@ public final class GeofenceHardwareImpl { "Location: " + geofenceTransition.mLocation + ":" + mGeofences); try { - callback.onGeofenceChange( + callback.onGeofenceTransition( geofenceTransition.mGeofenceId, geofenceTransition.mTransition, geofenceTransition.mLocation, geofenceTransition.mTimestamp, GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE); } catch (RemoteException e) {} releaseWakeLock(); break; + case GEOFENCE_CALLBACK_BINDER_DIED: + // Find all geofences associated with this callback and remove them. + callback = (IGeofenceHardwareCallback) (msg.obj); + if (DEBUG) Log.d(TAG, "Geofence callback reaped:" + callback); + int monitoringType = msg.arg1; + for (int i = 0; i < mGeofences.size(); i++) { + if (mGeofences.valueAt(i).equals(callback)) { + geofenceId = mGeofences.keyAt(i); + removeGeofence(mGeofences.keyAt(i), monitoringType); + mGeofences.remove(geofenceId); + } + } } } }; @@ -493,8 +537,8 @@ public final class GeofenceHardwareImpl { @Override public void handleMessage(Message msg) { int monitoringType; - ArrayList<IGeofenceHardwareCallback> callbackList; - IGeofenceHardwareCallback callback; + ArrayList<IGeofenceHardwareMonitorCallback> callbackList; + IGeofenceHardwareMonitorCallback callback; switch (msg.what) { case GPS_GEOFENCE_STATUS: @@ -508,7 +552,7 @@ public final class GeofenceHardwareImpl { if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available); - for (IGeofenceHardwareCallback c: callbackList) { + for (IGeofenceHardwareMonitorCallback c: callbackList) { try { c.onMonitoringSystemChange( GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available, @@ -519,22 +563,71 @@ public final class GeofenceHardwareImpl { break; case CALLBACK_ADD: monitoringType = msg.arg1; - callback = (IGeofenceHardwareCallback) msg.obj; + callback = (IGeofenceHardwareMonitorCallback) msg.obj; callbackList = mCallbacks[monitoringType]; if (callbackList == null) { - callbackList = new ArrayList<IGeofenceHardwareCallback>(); + callbackList = new ArrayList<IGeofenceHardwareMonitorCallback>(); mCallbacks[monitoringType] = callbackList; } if (!callbackList.contains(callback)) callbackList.add(callback); break; case CALLBACK_REMOVE: monitoringType = msg.arg1; - callback = (IGeofenceHardwareCallback) msg.obj; + callback = (IGeofenceHardwareMonitorCallback) msg.obj; callbackList = mCallbacks[monitoringType]; if (callbackList != null) { callbackList.remove(callback); } break; + case MONITOR_CALLBACK_BINDER_DIED: + callback = (IGeofenceHardwareMonitorCallback) msg.obj; + if (DEBUG) Log.d(TAG, "Monitor callback reaped:" + callback); + callbackList = mCallbacks[msg.arg1]; + if (callbackList != null && callbackList.contains(callback)) { + callbackList.remove(callback); + } + } + } + }; + + // All operations on mReaper + private Handler mReaperHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + Reaper r; + IGeofenceHardwareCallback callback; + IGeofenceHardwareMonitorCallback monitorCallback; + int monitoringType; + + switch (msg.what) { + case REAPER_GEOFENCE_ADDED: + callback = (IGeofenceHardwareCallback) msg.obj; + monitoringType = msg.arg1; + r = new Reaper(callback, monitoringType); + if (!mReapers.contains(r)) { + mReapers.add(r); + IBinder b = callback.asBinder(); + try { + b.linkToDeath(r, 0); + } catch (RemoteException e) {} + } + break; + case REAPER_MONITOR_CALLBACK_ADDED: + monitorCallback = (IGeofenceHardwareMonitorCallback) msg.obj; + monitoringType = msg.arg1; + + r = new Reaper(monitorCallback, monitoringType); + if (!mReapers.contains(r)) { + mReapers.add(r); + IBinder b = monitorCallback.asBinder(); + try { + b.linkToDeath(r, 0); + } catch (RemoteException e) {} + } + break; + case REAPER_REMOVED: + r = (Reaper) msg.obj; + mReapers.remove(r); } } }; @@ -567,6 +660,57 @@ public final class GeofenceHardwareImpl { return RESOLUTION_LEVEL_NONE; } + class Reaper implements IBinder.DeathRecipient { + private IGeofenceHardwareMonitorCallback mMonitorCallback; + private IGeofenceHardwareCallback mCallback; + private int mMonitoringType; + + Reaper(IGeofenceHardwareCallback c, int monitoringType) { + mCallback = c; + mMonitoringType = monitoringType; + } + + Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) { + mMonitorCallback = c; + mMonitoringType = monitoringType; + } + + @Override + public void binderDied() { + Message m; + if (mCallback != null) { + m = mGeofenceHandler.obtainMessage(GEOFENCE_CALLBACK_BINDER_DIED, mCallback); + m.arg1 = mMonitoringType; + mGeofenceHandler.sendMessage(m); + } else if (mMonitorCallback != null) { + m = mCallbacksHandler.obtainMessage(MONITOR_CALLBACK_BINDER_DIED, mMonitorCallback); + m.arg1 = mMonitoringType; + mCallbacksHandler.sendMessage(m); + } + Message reaperMessage = mReaperHandler.obtainMessage(REAPER_REMOVED, this); + mReaperHandler.sendMessage(reaperMessage); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); + result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.hashCode() : 0); + result = 31 * result + mMonitoringType; + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + + Reaper rhs = (Reaper) obj; + return rhs.mCallback == mCallback && rhs.mMonitorCallback == mMonitorCallback && + rhs.mMonitoringType == mMonitoringType; + } + } + int getAllowedResolutionLevel(int pid, int uid) { if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, pid, uid) == PackageManager.PERMISSION_GRANTED) { diff --git a/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java b/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java new file mode 100644 index 0000000..b8e927e --- /dev/null +++ b/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 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.hardware.location; + +import android.location.Location; + +/** + * The callback class associated with the status change of hardware montiors + * in {@link GeofenceHardware} + */ +public abstract class GeofenceHardwareMonitorCallback { + /** + * The callback called when the state of a monitoring system changes. + * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a + * monitoring system + * + * @param monitoringType The type of the monitoring system. + * @param available Indicates whether the system is currenty available or not. + * @param location The last known location according to the monitoring system. + */ + public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) { + } +} diff --git a/core/java/android/hardware/location/GeofenceHardwareRequest.java b/core/java/android/hardware/location/GeofenceHardwareRequest.java new file mode 100644 index 0000000..6e7b592 --- /dev/null +++ b/core/java/android/hardware/location/GeofenceHardwareRequest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 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.hardware.location; + +import android.location.Location; + +/** + * This class represents the characteristics of the geofence. + * + * <p> Use this in conjunction with {@link GeofenceHardware} APIs. + */ + +public final class GeofenceHardwareRequest { + static final int GEOFENCE_TYPE_CIRCLE = 0; + private int mType; + private double mLatitude; + private double mLongitude; + private double mRadius; + private int mLastTransition = GeofenceHardware.GEOFENCE_UNCERTAIN; + private int mUnknownTimer = 30000; // 30 secs + private int mMonitorTransitions = GeofenceHardware.GEOFENCE_UNCERTAIN | + GeofenceHardware.GEOFENCE_ENTERED | GeofenceHardware.GEOFENCE_EXITED; + private int mNotificationResponsiveness = 5000; // 5 secs + + private void setCircularGeofence(double latitude, double longitude, double radius) { + mLatitude = latitude; + mLongitude = longitude; + mRadius = radius; + mType = GEOFENCE_TYPE_CIRCLE; + } + + /** + * Create a circular geofence. + * + * @param latitude Latitude of the geofence + * @param longitude Longitude of the geofence + * @param radius Radius of the geofence (in meters) + */ + public static GeofenceHardwareRequest createCircularGeofence(double latitude, + double longitude, double radius) { + GeofenceHardwareRequest geofenceRequest = new GeofenceHardwareRequest(); + geofenceRequest.setCircularGeofence(latitude, longitude, radius); + return geofenceRequest; + } + + /** + * Set the last known transition of the geofence. + * + * @param lastTransition The current state of the geofence. Can be one of + * {@link GeofenceHardware#GEOFENCE_ENTERED}, {@link GeofenceHardware#GEOFENCE_EXITED}, + * {@link GeofenceHardware#GEOFENCE_UNCERTAIN}. + */ + public void setLastTransition(int lastTransition) { + mLastTransition = lastTransition; + } + + /** + * Set the unknown timer for this geofence. + * + * @param unknownTimer The time limit after which the + * {@link GeofenceHardware#GEOFENCE_UNCERTAIN} transition + * should be triggered. This paramter is defined in milliseconds. + */ + public void setUnknownTimer(int unknownTimer) { + mUnknownTimer = unknownTimer; + } + + /** + * Set the transitions to be monitored. + * + * @param monitorTransitions Bitwise OR of {@link GeofenceHardware#GEOFENCE_ENTERED}, + * {@link GeofenceHardware#GEOFENCE_EXITED}, {@link GeofenceHardware#GEOFENCE_UNCERTAIN} + */ + public void setMonitorTransitions(int monitorTransitions) { + mMonitorTransitions = monitorTransitions; + } + + /** + * Set the notification responsiveness of the geofence. + * + * @param notificationResponsiveness (milliseconds) Defines the best-effort description + * of how soon should the callback be called when the transition + * associated with the Geofence is triggered. For instance, if + * set to 1000 millseconds with {@link GeofenceHardware#GEOFENCE_ENTERED}, + * the callback will be called 1000 milliseconds within entering + * the geofence. + */ + public void setNotificationResponsiveness(int notificationResponsiveness) { + mNotificationResponsiveness = notificationResponsiveness; + } + + /** + * Returns the latitude of this geofence. + */ + public double getLatitude() { + return mLatitude; + } + + /** + * Returns the longitude of this geofence. + */ + public double getLongitude() { + return mLongitude; + } + + /** + * Returns the radius of this geofence. + */ + public double getRadius() { + return mRadius; + } + + /** + * Returns transitions monitored for this geofence. + */ + public int getMonitorTransitions() { + return mMonitorTransitions; + } + + /** + * Returns the unknownTimer of this geofence. + */ + public int getUnknownTimer() { + return mUnknownTimer; + } + + /** + * Returns the notification responsiveness of this geofence. + */ + public int getNotificationResponsiveness() { + return mNotificationResponsiveness; + } + + /** + * Returns the last transition of this geofence. + */ + public int getLastTransition() { + return mLastTransition; + } + + int getType() { + return mType; + } +} diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java index 0eccee6..3bc70ee 100644 --- a/core/java/android/hardware/location/GeofenceHardwareService.java +++ b/core/java/android/hardware/location/GeofenceHardwareService.java @@ -68,23 +68,28 @@ public class GeofenceHardwareService extends Service { mGeofenceHardwareImpl.setGpsHardwareGeofence(service); } - public int[] getMonitoringTypesAndStatus() { + public int[] getMonitoringTypes() { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); - return mGeofenceHardwareImpl.getMonitoringTypesAndStatus(); + return mGeofenceHardwareImpl.getMonitoringTypes(); } - public boolean addCircularFence(int id, double lat, double longitude, double radius, - int lastTransition, int monitorTransitions, int - notificationResponsiveness, int unknownTimer, int monitoringType, - IGeofenceHardwareCallback callback) { + public int getStatusOfMonitoringType(int monitoringType) { + mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to access hardware geofence"); + + return mGeofenceHardwareImpl.getStatusOfMonitoringType(monitoringType); + } + public boolean addCircularFence(int id, int monitoringType, double lat, double longitude, + double radius, int lastTransition, int monitorTransitions, int + notificationResponsiveness, int unknownTimer, IGeofenceHardwareCallback callback) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); - return mGeofenceHardwareImpl.addCircularFence(id, lat, longitude, radius, - lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer, - monitoringType, callback); + return mGeofenceHardwareImpl.addCircularFence(id, monitoringType, lat, longitude, + radius, lastTransition, monitorTransitions, notificationResponsiveness, + unknownTimer, callback); } public boolean removeGeofence(int id, int monitoringType) { @@ -103,16 +108,16 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType); } - public boolean resumeGeofence(int id, int monitorTransitions, int monitoringType) { + public boolean resumeGeofence(int id, int monitoringType, int monitorTransitions) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); - return mGeofenceHardwareImpl.resumeGeofence(id, monitorTransitions, monitoringType); + return mGeofenceHardwareImpl.resumeGeofence(id, monitoringType, monitorTransitions); } public boolean registerForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback) { + IGeofenceHardwareMonitorCallback callback) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); @@ -122,7 +127,7 @@ public class GeofenceHardwareService extends Service { } public boolean unregisterForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback) { + IGeofenceHardwareMonitorCallback callback) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl index 4ba02b8..6900070 100644 --- a/core/java/android/hardware/location/IGeofenceHardware.aidl +++ b/core/java/android/hardware/location/IGeofenceHardware.aidl @@ -18,19 +18,21 @@ package android.hardware.location; import android.location.IGpsGeofenceHardware; import android.hardware.location.IGeofenceHardwareCallback; +import android.hardware.location.IGeofenceHardwareMonitorCallback; /** @hide */ interface IGeofenceHardware { void setGpsGeofenceHardware(in IGpsGeofenceHardware service); - int[] getMonitoringTypesAndStatus(); - boolean addCircularFence(int id, double lat, double longitude, double radius, - int lastTransition, int monitorTransitions, int notificationResponsiveness, - int unknownTimer, int monitoringType, in IGeofenceHardwareCallback callback); + int[] getMonitoringTypes(); + int getStatusOfMonitoringType(int monitoringType); + boolean addCircularFence(int id, int monitoringType, double lat, double longitude, + double radius, int lastTransition, int monitorTransitions, + int notificationResponsiveness, int unknownTimer,in IGeofenceHardwareCallback callback); boolean removeGeofence(int id, int monitoringType); boolean pauseGeofence(int id, int monitoringType); - boolean resumeGeofence(int id, int monitorTransitions, int monitoringType); + boolean resumeGeofence(int id, int monitoringType, int monitorTransitions); boolean registerForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback); + IGeofenceHardwareMonitorCallback callback); boolean unregisterForMonitorStateChangeCallback(int monitoringType, - IGeofenceHardwareCallback callback); + IGeofenceHardwareMonitorCallback callback); } diff --git a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl index 678fc49..3a8f430 100644 --- a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl +++ b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl @@ -20,8 +20,7 @@ import android.location.Location; /** @hide */ oneway interface IGeofenceHardwareCallback { - void onMonitoringSystemChange(int monitoringType, boolean available, in Location location); - void onGeofenceChange(int geofenceId, int transition, in Location location, + void onGeofenceTransition(int geofenceId, int transition, in Location location, long timestamp, int monitoringType); void onGeofenceAdd(int geofenceId, int status); void onGeofenceRemove(int geofenceId, int status); diff --git a/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl new file mode 100644 index 0000000..0b6e04b --- /dev/null +++ b/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.hardware.location; + +import android.location.Location; + +/** @hide */ +oneway interface IGeofenceHardwareMonitorCallback { + void onMonitoringSystemChange(int monitoringType, boolean available, in Location location); +} diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 2e8092a..a11358a 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -42,7 +42,8 @@ interface IUserManager { int getUserHandle(int userSerialNumber); Bundle getUserRestrictions(int userHandle); void setUserRestrictions(in Bundle restrictions, int userHandle); - void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries, + void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); - List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle); + Bundle getApplicationRestrictions(in String packageName); + Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e580e2b..df065e9 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -142,6 +142,7 @@ public class UserManager { private static UserManager sInstance = null; + /** @hide */ public synchronized static UserManager get(Context context) { if (sInstance == null) { sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE); @@ -578,13 +579,29 @@ public class UserManager { return -1; } + /** + * Returns a Bundle containing any saved application restrictions for this user, for the + * given package name. Only an application with this package name can call this method. + * @param packageName the package name of the calling application + * @return a Bundle with the restrictions as key/value pairs, or null if there are no + * saved restrictions. The values can be of type Boolean, String or String[], depending + * on the restriction type, as defined by the application. + */ + public Bundle getApplicationRestrictions(String packageName) { + try { + return mService.getApplicationRestrictions(packageName); + } catch (RemoteException re) { + Log.w(TAG, "Could not get application restrictions for package " + packageName); + } + return null; + } /** * @hide */ - public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) { + public Bundle getApplicationRestrictions(String packageName, UserHandle user) { try { - return mService.getApplicationRestrictions(packageName, user.getIdentifier()); + return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier()); } catch (RemoteException re) { Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier()); } @@ -594,10 +611,10 @@ public class UserManager { /** * @hide */ - public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries, + public void setApplicationRestrictions(String packageName, Bundle restrictions, UserHandle user) { try { - mService.setApplicationRestrictions(packageName, entries, user.getIdentifier()); + mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier()); } catch (RemoteException re) { Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier()); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 88ee414..3df4e99 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3260,6 +3260,7 @@ public final class Settings { /** * This preference contains the string that shows for owner info on LockScreen. * @hide + * @deprecated */ public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info"; @@ -3287,6 +3288,7 @@ public final class Settings { /** * This preference enables showing the owner info on LockScreen. * @hide + * @deprecated */ public static final String LOCK_SCREEN_OWNER_INFO_ENABLED = "lock_screen_owner_info_enabled"; @@ -4102,9 +4104,7 @@ public final class Settings { MOUNT_UMS_AUTOSTART, MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, - UI_NIGHT_MODE, - LOCK_SCREEN_OWNER_INFO, - LOCK_SCREEN_OWNER_INFO_ENABLED + UI_NIGHT_MODE }; /** diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index edfef56..4989c3a 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -220,14 +220,14 @@ public class Surface implements Parcelable { /** * Gets a {@link Canvas} for drawing into this surface. * - * After drawing into the provided {@link Canvas}, the caller should + * After drawing into the provided {@link Canvas}, the caller must * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. * * @param inOutDirty A rectangle that represents the dirty region that the caller wants * to redraw. This function may choose to expand the dirty rectangle if for example * the surface has been resized or if the previous contents of the surface were - * not available. The caller should redraw the entire dirty region as represented - * by the contents of the dirty rect upon return from this function. + * not available. The caller must redraw the entire dirty region as represented + * by the contents of the inOutDirty rectangle upon return from this function. * The caller may also pass <code>null</code> instead, in the case where the * entire surface should be redrawn. * @return A canvas for drawing into the surface. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 14fa9cb..793fb5e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -755,12 +755,36 @@ public class SurfaceView extends View { mHandler.sendMessage(msg); } + /** + * Gets a {@link Canvas} for drawing into the SurfaceView's Surface + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * The caller must redraw the entire surface. + * @return A canvas for drawing into the surface. + */ public Canvas lockCanvas() { return internalLockCanvas(null); } - public Canvas lockCanvas(Rect dirty) { - return internalLockCanvas(dirty); + /** + * Gets a {@link Canvas} for drawing into the SurfaceView's Surface + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * @param inOutDirty A rectangle that represents the dirty region that the caller wants + * to redraw. This function may choose to expand the dirty rectangle if for example + * the surface has been resized or if the previous contents of the surface were + * not available. The caller must redraw the entire dirty region as represented + * by the contents of the inOutDirty rectangle upon return from this function. + * The caller may also pass <code>null</code> instead, in the case where the + * entire surface should be redrawn. + * @return A canvas for drawing into the surface. + */ + public Canvas lockCanvas(Rect inOutDirty) { + return internalLockCanvas(inOutDirty); } private final Canvas internalLockCanvas(Rect dirty) { @@ -810,6 +834,12 @@ public class SurfaceView extends View { return null; } + /** + * Posts the new contents of the {@link Canvas} to the surface and + * releases the {@link Canvas}. + * + * @param canvas The canvas previously obtained from {@link #lockCanvas}. + */ public void unlockCanvasAndPost(Canvas canvas) { mSurface.unlockCanvasAndPost(canvas); mSurfaceLock.unlock(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4fb2431..be26d20 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13380,18 +13380,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Sets a rectangular area on this view to which the view will be clipped - * it is drawn. Setting the value to null will remove the clip bounds + * when it is drawn. Setting the value to null will remove the clip bounds * and the view will draw normally, using its full bounds. * * @param clipBounds The rectangular area, in the local coordinates of * this view, to which future drawing operations will be clipped. */ public void setClipBounds(Rect clipBounds) { - mClipBounds = clipBounds; if (clipBounds != null) { - invalidate(clipBounds); + if (clipBounds.equals(mClipBounds)) { + return; + } + if (mClipBounds == null) { + invalidate(); + mClipBounds = new Rect(clipBounds); + } else { + invalidate(Math.min(mClipBounds.left, clipBounds.left), + Math.min(mClipBounds.top, clipBounds.top), + Math.max(mClipBounds.right, clipBounds.right), + Math.max(mClipBounds.bottom, clipBounds.bottom)); + mClipBounds.set(clipBounds); + } } else { - invalidate(); + if (mClipBounds != null) { + invalidate(); + mClipBounds = null; + } } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 83e2e79..07e66b7 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2108,6 +2108,8 @@ public class RemoteViews implements Parcelable, Filter { * RemoteViews. This count cannot change during the life-cycle of a given widget, so this * parameter should account for the maximum possible number of types that may appear in the * See {@link Adapter#getViewTypeCount()}. + * + * @hide */ public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) { addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount)); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 555c7c2..d3ead26 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -140,6 +140,10 @@ public class LockPatternUtils { public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; + private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO; + private static final String LOCK_SCREEN_OWNER_INFO_ENABLED = + Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; + private final Context mContext; private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; @@ -526,6 +530,22 @@ public class LockPatternUtils { } } + public void setOwnerInfo(String info, int userId) { + setString(LOCK_SCREEN_OWNER_INFO, info, userId); + } + + public void setOwnerInfoEnabled(boolean enabled) { + setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled); + } + + public String getOwnerInfo(int userId) { + return getString(LOCK_SCREEN_OWNER_INFO); + } + + public boolean isOwnerInfoEnabled() { + return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false); + } + /** * Compute the password quality from the given password string. */ diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 1ace23e..17f205d 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -339,23 +339,11 @@ uint32_t TextLayoutValue::getElapsedTime() { } TextLayoutShaper::TextLayoutShaper() { - init(); - mBuffer = hb_buffer_create(); } -void TextLayoutShaper::init() { - mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal); -} - -void TextLayoutShaper::unrefTypefaces() { - SkSafeUnref(mDefaultTypeface); -} - TextLayoutShaper::~TextLayoutShaper() { hb_buffer_destroy(mBuffer); - - unrefTypefaces(); } void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars, @@ -839,23 +827,27 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) { } if (baseGlyphCount != 0) { + SkTypeface::Style style = SkTypeface::kNormal; + if (typeface != NULL) { + style = typeface->style(); + } typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer)); if (!typeface) { baseGlyphCount = 0; - typeface = mDefaultTypeface; - SkSafeRef(typeface); + typeface = SkFontHost::CreateTypeface(NULL, NULL, style); #if DEBUG_GLYPHS ALOGD("Using Default Typeface"); #endif } } else { if (!typeface) { - typeface = mDefaultTypeface; + typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal); #if DEBUG_GLYPHS - ALOGD("Using Default Typeface"); + ALOGD("Using Default Typeface (normal style)"); #endif + } else { + SkSafeRef(typeface); } - SkSafeRef(typeface); } mShapingPaint.setTypeface(typeface); @@ -899,8 +891,6 @@ void TextLayoutShaper::purgeCaches() { hb_face_destroy(mCachedHBFaces.valueAt(i)); } mCachedHBFaces.clear(); - unrefTypefaces(); - init(); } TextLayoutEngine::TextLayoutEngine() { diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index f9b9900..5414a11 100644 --- a/core/jni/android/graphics/TextLayoutCache.h +++ b/core/jni/android/graphics/TextLayoutCache.h @@ -197,18 +197,10 @@ private: SkPaint mShapingPaint; /** - * Skia default typeface to be returned if we cannot resolve script - */ - SkTypeface* mDefaultTypeface; - - /** * Cache of Harfbuzz faces */ KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces; - void init(); - void unrefTypefaces(); - SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface, hb_script_t script); @@ -228,7 +220,6 @@ private: hb_face_t* referenceCachedHBFace(SkTypeface* typeface); bool isComplexScript(hb_script_t script); - }; // TextLayoutShaper /** diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6b4fe79..514178d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -918,7 +918,8 @@ <permission android:name="android.permission.RECORD_AUDIO" android:permissionGroup="android.permission-group.MICROPHONE" android:protectionLevel="dangerous" - android:label="@string/permlab_recordAudio" /> + android:label="@string/permlab_recordAudio" + android:description="@string/permdesc_recordAudio" /> <!-- =========================================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cc50d8a..84e300a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -345,6 +345,12 @@ A value of -1 means no change in orientation by default. --> <integer name="config_carDockRotation">-1</integer> + <!-- The number of degrees to rotate the display when the device has HDMI connected + but is not in a dock. A value of -1 means no change in orientation by default. + Use -1 except on older devices whose Hardware Composer HAL does not + provide full support for multiple displays. --> + <integer name="config_undockedHdmiRotation">-1</integer> + <!-- Control the default UI mode type to use when there is no other type override happening. One of the following values (See Configuration.java): 1 UI_MODE_TYPE_NORMAL diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 45ea182..5e75390 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1584,6 +1584,7 @@ <java-symbol type="integer" name="config_screenBrightnessSettingDefault" /> <java-symbol type="integer" name="config_screenBrightnessDim" /> <java-symbol type="integer" name="config_shutdownBatteryTemperature" /> + <java-symbol type="integer" name="config_undockedHdmiRotation" /> <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> <java-symbol type="layout" name="am_compat_mode_dialog" /> <java-symbol type="layout" name="launch_warning" /> diff --git a/data/keyboards/Vendor_0079_Product_0011.kl b/data/keyboards/Vendor_0079_Product_0011.kl new file mode 100644 index 0000000..2ae2a01 --- /dev/null +++ b/data/keyboards/Vendor_0079_Product_0011.kl @@ -0,0 +1,23 @@ +# Copyright (C) 2013 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. + +# Classic NES Controller + +key 289 BUTTON_A +key 290 BUTTON_B +key 297 BUTTON_START +key 296 BUTTON_SELECT + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/data/keyboards/Vendor_045e_Product_028e.kl b/data/keyboards/Vendor_045e_Product_028e.kl index 99f046a..ca6fa59 100644 --- a/data/keyboards/Vendor_045e_Product_028e.kl +++ b/data/keyboards/Vendor_045e_Product_028e.kl @@ -22,9 +22,9 @@ key 307 BUTTON_X key 308 BUTTON_Y key 310 BUTTON_L1 key 311 BUTTON_R1 -key 314 BUTTON_SELECT +key 314 BUTTON_BACK key 315 BUTTON_START -key 316 BUTTON_MODE +key 316 BUTTON_HOME key 317 BUTTON_THUMBL key 318 BUTTON_THUMBR diff --git a/data/keyboards/Vendor_046d_Product_c219.kl b/data/keyboards/Vendor_046d_Product_c219.kl new file mode 100644 index 0000000..431dd03 --- /dev/null +++ b/data/keyboards/Vendor_046d_Product_c219.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2013 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. + +# Logitech Logitech Cordless RumblePad 2 + +key 305 BUTTON_A +key 306 BUTTON_B +key 304 BUTTON_X +key 307 BUTTON_Y +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 310 BUTTON_L2 +key 311 BUTTON_R2 +key 313 BUTTON_START +key 312 BUTTON_BACK +key 314 BUTTON_THUMBL +key 315 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_046d_Product_c21f.kl b/data/keyboards/Vendor_046d_Product_c21f.kl new file mode 100644 index 0000000..981d864 --- /dev/null +++ b/data/keyboards/Vendor_046d_Product_c21f.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# Logitech Wireless Gamepad F710 + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl index f8ac6a3..62c5f4d 100644 --- a/data/keyboards/Vendor_054c_Product_0268.kl +++ b/data/keyboards/Vendor_054c_Product_0268.kl @@ -23,10 +23,10 @@ key 0x127 DPAD_LEFT key 0x120 BUTTON_SELECT key 0x123 BUTTON_START -key 0x12f BUTTON_A -key 0x12c BUTTON_B -key 0x12e BUTTON_X -key 0x12d BUTTON_Y +key 0x12e BUTTON_A +key 0x12d BUTTON_B +key 0x12f BUTTON_X +key 0x12c BUTTON_Y key 0x12a BUTTON_L1 key 0x12b BUTTON_R1 key 0x128 BUTTON_L2 @@ -35,7 +35,7 @@ key 0x121 BUTTON_THUMBL key 0x122 BUTTON_THUMBR # PS key -key 0x2d0 BUTTON_1 +key 0x2d0 BUTTON_HOME # Left Analog Stick axis 0x00 X diff --git a/data/keyboards/Vendor_0583_Product_2060.kl b/data/keyboards/Vendor_0583_Product_2060.kl new file mode 100644 index 0000000..92c8a14 --- /dev/null +++ b/data/keyboards/Vendor_0583_Product_2060.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2013 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. + +# ION GO PAD + +key 288 BUTTON_A +key 289 BUTTON_B +key 290 BUTTON_X +key 291 BUTTON_Y +key 294 BUTTON_L1 +key 295 BUTTON_R1 +key 292 BUTTON_L2 +key 293 BUTTON_R2 + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/data/keyboards/Vendor_0a5c_Product_8502.kl b/data/keyboards/Vendor_0a5c_Product_8502.kl new file mode 100644 index 0000000..0084969 --- /dev/null +++ b/data/keyboards/Vendor_0a5c_Product_8502.kl @@ -0,0 +1,33 @@ +# Copyright (C) 2013 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. + +# Snakebyte + +key 289 BUTTON_A +key 290 BUTTON_B +key 288 BUTTON_X +key 291 BUTTON_Y +key 292 BUTTON_L1 +key 293 BUTTON_R1 +key 294 BUTTON_L2 +key 295 BUTTON_R2 +key 297 BUTTON_START +key 296 BUTTON_SELECT + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1038_Product_1412.kl b/data/keyboards/Vendor_1038_Product_1412.kl new file mode 100644 index 0000000..551b0bd --- /dev/null +++ b/data/keyboards/Vendor_1038_Product_1412.kl @@ -0,0 +1,31 @@ +# Copyright (C) 2013 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. + +# Steelseries Free + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 316 BUTTON_SELECT + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_12bd_Product_d015.kl b/data/keyboards/Vendor_12bd_Product_d015.kl new file mode 100644 index 0000000..557d62f --- /dev/null +++ b/data/keyboards/Vendor_12bd_Product_d015.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2013 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. + +# Hitgaming SNES Retro + +key 306 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 304 BUTTON_Y +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 313 BUTTON_START +key 312 BUTTON_SELECT + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/data/keyboards/Vendor_1689_Product_fd00.kl b/data/keyboards/Vendor_1689_Product_fd00.kl new file mode 100644 index 0000000..6ce14ed --- /dev/null +++ b/data/keyboards/Vendor_1689_Product_fd00.kl @@ -0,0 +1,38 @@ +# Copyright (C) 2013 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. + +# Razer Onza Tournament Edition + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 307 BUTTON_L1 +key 308 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR +key 706 DPAD_UP +key 705 DPAD_RIGHT +key 707 DPAD_DOWN +key 704 DPAD_LEFT + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER diff --git a/data/keyboards/Vendor_1689_Product_fd01.kl b/data/keyboards/Vendor_1689_Product_fd01.kl new file mode 100644 index 0000000..8144515 --- /dev/null +++ b/data/keyboards/Vendor_1689_Product_fd01.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# Razer Xbox 360 Gamepad + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 308 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1689_Product_fe00.kl b/data/keyboards/Vendor_1689_Product_fe00.kl new file mode 100644 index 0000000..90fe4af --- /dev/null +++ b/data/keyboards/Vendor_1689_Product_fe00.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# Razer Sabertooth Elite + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1bad_Product_f016.kl b/data/keyboards/Vendor_1bad_Product_f016.kl new file mode 100644 index 0000000..b72fd5c --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f016.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# Madcatz Gamepad + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1bad_Product_f023.kl b/data/keyboards/Vendor_1bad_Product_f023.kl new file mode 100644 index 0000000..c1588b2 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f023.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2013 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. + +# Mad Catz MLG GamePad for Xbox 360 + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1bad_Product_f027.kl b/data/keyboards/Vendor_1bad_Product_f027.kl new file mode 100644 index 0000000..ea0aa7a --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f027.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# MadCatz FPS Pro + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1bad_Product_f036.kl b/data/keyboards/Vendor_1bad_Product_f036.kl new file mode 100644 index 0000000..8cd906a --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f036.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# MadCatz Generic XBox Controller + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_BACK +key 316 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_1d79_Product_0009.kl b/data/keyboards/Vendor_1d79_Product_0009.kl new file mode 100644 index 0000000..78fe2cd --- /dev/null +++ b/data/keyboards/Vendor_1d79_Product_0009.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 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. + +# Nyko Playpad / Playpad Pro + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 158 BUTTON_BACK +key 172 BUTTON_HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/data/keyboards/Vendor_2378_Product_100a.kl b/data/keyboards/Vendor_2378_Product_100a.kl new file mode 100644 index 0000000..d9cd171 --- /dev/null +++ b/data/keyboards/Vendor_2378_Product_100a.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2013 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. + +# OnLive, Inc. OnLive Wireless Controller + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_SELECT +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/docs/html/images/mediadrm_decryption_sequence.png b/docs/html/images/mediadrm_decryption_sequence.png Binary files differnew file mode 100644 index 0000000..2bd95ea --- /dev/null +++ b/docs/html/images/mediadrm_decryption_sequence.png diff --git a/docs/html/images/mediadrm_overview.png b/docs/html/images/mediadrm_overview.png Binary files differnew file mode 100644 index 0000000..dd66bce --- /dev/null +++ b/docs/html/images/mediadrm_overview.png diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 6872278..31fbc4a 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -29,25 +29,69 @@ import android.os.Parcel; import android.util.Log; /** - * MediaDrm can be used in conjunction with {@link android.media.MediaCrypto} - * to obtain keys for decrypting protected media data. - * - * Crypto schemes are assigned 16 byte UUIDs, - * the method {@link #isCryptoSchemeSupported} can be used to query if a given - * scheme is supported on the device. - * + * MediaDrm can be used to obtain keys for decrypting protected media streams, in + * conjunction with {@link android.media.MediaCrypto}. The MediaDrm APIs + * are designed to support the ISO/IEC 23001-7: Common Encryption standard, but + * may also be used to implement other encryption schemes. + * <p> + * Encrypted content is prepared using an encryption server and stored in a content + * library. The encrypted content is streamed or downloaded from the content library to + * client devices via content servers. Licenses to view the content are obtained from + * a License Server. + * <p> + * <p><img src="../../../images/mediadrm_overview.png" + * alt="MediaDrm Overview diagram" + * border="0" /></p> + * <p> + * Keys are requested from the license server using a key request. The key + * response is delivered to the client app, which provides the response to the + * MediaDrm API. + * <p> + * A Provisioning server may be required to distribute device-unique credentials to + * the devices. + * <p> + * Enforcing requirements related to the number of devices that may play content + * simultaneously can be performed either through key renewal or using the secure + * stop methods. + * <p> + * The following sequence diagram shows the interactions between the objects + * involved while playing back encrypted content: + * <p> + * <p><img src="../../../images/mediadrm_decryption_sequence.png" + * alt="MediaDrm Overview diagram" + * border="0" /></p> + * <p> + * The app first constructs {@link android.media.MediaExtractor} and + * {@link android.media.MediaCodec} objects. It accesses the DRM-scheme-identifying UUID, + * typically from metadata in the content, and uses this UUID to construct an instance + * of a MediaDrm object that is able to support the DRM scheme required by the content. + * Crypto schemes are assigned 16 byte UUIDs. The method {@link #isCryptoSchemeSupported} + * can be used to query if a given scheme is supported on the device. + * <p> + * The app calls {@link #openSession} to generate a sessionId that will uniquely identify + * the session in subsequent interactions. The app next uses the MediaDrm object to + * obtain a key request message and send it to the license server, then provide + * the server's response to the MediaDrm object. + * <p> + * Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and + * sessionId. The MediaCrypto object is registered with the MediaCodec in the + * {@link MediaCodec.#configure} method to enable the codec to decrypt content. + * <p> + * When the app has constructed {@link android.media.MediaExtractor}, + * {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects, + * it proceeds to pull samples from the extractor and queue them into the decoder. For + * encrypted content, the samples returned from the extractor remain encrypted, they + * are only decrypted when the samples are delivered to the decoder. + * <p> * <a name="Callbacks"></a> * <h3>Callbacks</h3> - * <p>Applications may want to register for informational events in order - * to be informed of some internal state update during playback or streaming. + * <p>Applications should register for informational events in order + * to be informed of key state updates during playback or streaming. * Registration for these events is done via a call to - * {@link #setOnEventListener(OnInfoListener)}setOnInfoListener, - * In order to receive the respective callback - * associated with this listener, applications are required to create + * {@link #setOnEventListener}. In order to receive the respective + * callback associated with this listener, applications are required to create * MediaDrm objects on a thread with its own Looper running (main UI * thread by default has a Looper running). - * - * @hide -- don't expose yet */ public final class MediaDrm { @@ -116,7 +160,7 @@ public final class MediaDrm { /** * Interface definition for a callback to be invoked when a drm event - * occurs. + * occurs */ public interface OnEventListener { @@ -132,10 +176,30 @@ public final class MediaDrm { void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data); } - public static final int MEDIA_DRM_EVENT_PROVISION_REQUIRED = 1; - public static final int MEDIA_DRM_EVENT_KEY_REQUIRED = 2; - public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3; - public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4; + /** + * This event type indicates that the app needs to request a certificate from + * the provisioning server. The request message data is obtained using + * {@link #getProvisionRequest} + */ + public static final int EVENT_PROVISION_REQUIRED = 1; + + /** + * This event type indicates that the app needs to request keys from a license + * server. The request message data is obtained using {@link #getKeyRequest}. + */ + public static final int EVENT_KEY_REQUIRED = 2; + + /** + * This event type indicates that the licensed usage duration for keys in a session + * has expired. The keys are no longer valid. + */ + public static final int EVENT_KEY_EXPIRED = 3; + + /** + * This event may indicate some specific vendor-defined condition, see your + * DRM provider documentation for details + */ + public static final int EVENT_VENDOR_DEFINED = 4; private static final int DRM_EVENT = 200; @@ -183,7 +247,7 @@ public final class MediaDrm { } /* - * Called from native code when an interesting event happens. This method + * This method is called from native code when an event occurs. This method * just uses the EventHandler system to post the event back to the main app thread. * We use a weak reference to the original MediaPlayer object so that the native * code is safe from the object disappearing from underneath it. (This is @@ -203,89 +267,117 @@ public final class MediaDrm { } /** - * Open a new session with the MediaDrm object. A session ID is returned. + * Open a new session with the MediaDrm object. A session ID is returned. */ - public native byte[] openSession() throws MediaDrmException; + public native byte[] openSession(); /** - * Close a session on the MediaDrm object that was previously opened - * with {@link #openSession}. + * Close a session on the MediaDrm object that was previously opened + * with {@link #openSession}. */ - public native void closeSession(byte[] sessionId) throws MediaDrmException; + public native void closeSession(byte[] sessionId); - public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1; - public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2; - public static final int MEDIA_DRM_KEY_TYPE_RELEASE = 3; + /** + * This key request type species that the keys will be for online use, they will + * not be saved to the device for subsequent use when the device is not connected + * to a network. + */ + public static final int KEY_TYPE_STREAMING = 1; + + /** + * This key request type specifies that the keys will be for offline use, they + * will be saved to the device for use when the device is not connected to a network. + */ + public static final int KEY_TYPE_OFFLINE = 2; + + /** + * This key request type specifies that previously saved offline keys should be released. + */ + public static final int KEY_TYPE_RELEASE = 3; + + /** + * Contains the opaque data an app uses to request keys from a license server + */ + public final static class KeyRequest { + KeyRequest() {} + + /** + * Get the opaque message data + */ + public byte[] getData() { return mData; } + + /** + * Get the default URL to use when sending the key request message to a + * server, if known. The app may prefer to use a different license + * server URL from other sources. + */ + public String getDefaultUrl() { return mDefaultUrl; } - public final class KeyRequest { - public KeyRequest() {} - public byte[] data; - public String defaultUrl; + private byte[] mData; + private String mDefaultUrl; }; /** * A key request/response exchange occurs between the app and a license server * to obtain or release keys used to decrypt encrypted content. + * <p> * getKeyRequest() is used to obtain an opaque key request byte array that is * delivered to the license server. The opaque key request byte array is returned * in KeyRequest.data. The recommended URL to deliver the key request to is * returned in KeyRequest.defaultUrl. - * + * <p> * After the app has received the key request response from the server, * it should deliver to the response to the DRM engine plugin using the method * {@link #provideKeyResponse}. * * @param scope may be a sessionId or a keySetId, depending on the specified keyType. - * When the keyType is MEDIA_DRM_KEY_TYPE_STREAMING or MEDIA_DRM_KEY_TYPE_OFFLINE, + * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, * scope should be set to the sessionId the keys will be provided to. When the keyType - * is MEDIA_DRM_KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys + * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys * being released. Releasing keys from a device invalidates them for all sessions. * @param init container-specific data, its meaning is interpreted based on the * mime type provided in the mimeType parameter. It could contain, for example, * the content ID, key ID or other data obtained from the content metadata that is * required in generating the key request. init may be null when keyType is - * MEDIA_DRM_KEY_TYPE_RELEASE. + * KEY_TYPE_RELEASE. * @param mimeType identifies the mime type of the content * @param keyType specifes the type of the request. The request may be to acquire * keys for streaming or offline content, or to release previously acquired * keys, which are identified by a keySetId. - * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. */ public native KeyRequest getKeyRequest(byte[] scope, byte[] init, String mimeType, int keyType, - HashMap<String, String> optionalParameters) - throws MediaDrmException; + HashMap<String, String> optionalParameters); + /** * A key response is received from the license server by the app, then it is * provided to the DRM engine plugin using provideKeyResponse. The byte array * returned is a keySetId that can be used to later restore the keys to a new - * session with the method {@link restoreKeys}, enabling offline key use. + * session with the method {@link #restoreKeys}, enabling offline key use. * * @param sessionId the session ID for the DRM session * @param response the byte array response from the server */ - public native byte[] provideKeyResponse(byte[] sessionId, byte[] response) - throws MediaDrmException; + public native byte[] provideKeyResponse(byte[] sessionId, byte[] response); /** * Restore persisted offline keys into a new session. keySetId identifies the - * keys to load, obtained from a prior call to {@link provideKeyResponse}. + * keys to load, obtained from a prior call to {@link #provideKeyResponse}. * * @param sessionId the session ID for the DRM session * @param keySetId identifies the saved key set to restore */ - public native void restoreKeys(byte[] sessionId, byte[] keySetId) - throws MediaDrmException; + public native void restoreKeys(byte[] sessionId, byte[] keySetId); /** * Remove the current keys from a session. * * @param sessionId the session ID for the DRM session */ - public native void removeKeys(byte[] sessionId) throws MediaDrmException; + public native void removeKeys(byte[] sessionId); /** * Request an informative description of the key status for the session. The status is @@ -296,25 +388,41 @@ public final class MediaDrm { * * @param sessionId the session ID for the DRM session */ - public native HashMap<String, String> queryKeyStatus(byte[] sessionId) - throws MediaDrmException; + public native HashMap<String, String> queryKeyStatus(byte[] sessionId); + + /** + * Contains the opaque data an app uses to request a certificate from a provisioning + * server + */ + public final static class ProvisionRequest { + ProvisionRequest() {} + + /** + * Get the opaque message data + */ + public byte[] getData() { return mData; } + + /** + * Get the default URL to use when sending the provision request + * message to a server, if known. The app may prefer to use a different + * provisioning server URL obtained from other sources. + */ + public String getDefaultUrl() { return mDefaultUrl; } - public final class ProvisionRequest { - public ProvisionRequest() {} - public byte[] data; - public String defaultUrl; + private byte[] mData; + private String mDefaultUrl; } /** * A provision request/response exchange occurs between the app and a provisioning * server to retrieve a device certificate. If provisionining is required, the - * MEDIA_DRM_EVENT_PROVISION_REQUIRED event will be sent to the event handler. + * EVENT_PROVISION_REQUIRED event will be sent to the event handler. * getProvisionRequest is used to obtain the opaque provision request byte array that * should be delivered to the provisioning server. The provision request byte array * is returned in ProvisionRequest.data. The recommended URL to deliver the provision * request to is returned in ProvisionRequest.defaultUrl. */ - public native ProvisionRequest getProvisionRequest() throws MediaDrmException; + public native ProvisionRequest getProvisionRequest(); /** * After a provision response is received by the app, it is provided to the DRM @@ -323,92 +431,91 @@ public final class MediaDrm { * @param response the opaque provisioning response byte array to provide to the * DRM engine plugin. */ - public native void provideProvisionResponse(byte[] response) - throws MediaDrmException; + public native void provideProvisionResponse(byte[] response); /** - * A means of enforcing the contractual requirement for a concurrent stream limit - * per subscriber across devices is provided via SecureStop. SecureStop is a means - * of securely monitoring the lifetime of sessions. Since playback on a device can - * be interrupted due to reboot, power failure, etc. a means of persisting the - * lifetime information on the device is needed. - * - * A signed version of the sessionID is written to persistent storage on the device - * when each MediaCrypto object is created. The sessionID is signed by the device - * private key to prevent tampering. - * + * A means of enforcing limits on the number of concurrent streams per subscriber + * across devices is provided via SecureStop. This is achieved by securely + * monitoring the lifetime of sessions. + * <p> + * Information from the server related to the current playback session is written + * to persistent storage on the device when each MediaCrypto object is created. + * <p> * In the normal case, playback will be completed, the session destroyed and the - * Secure Stops will be queried. The App queries secure stops and forwards the + * Secure Stops will be queried. The app queries secure stops and forwards the * secure stop message to the server which verifies the signature and notifies the * server side database that the session destruction has been confirmed. The persisted * record on the client is only removed after positive confirmation that the server * received the message using releaseSecureStops(). */ - public native List<byte[]> getSecureStops() throws MediaDrmException; + public native List<byte[]> getSecureStops(); /** * Process the SecureStop server response message ssRelease. After authenticating - * the message, remove the SecureStops identiied in the response. + * the message, remove the SecureStops identified in the response. * * @param ssRelease the server response indicating which secure stops to release */ - public native void releaseSecureStops(byte[] ssRelease) - throws MediaDrmException; + public native void releaseSecureStops(byte[] ssRelease); /** - * Read a DRM engine plugin property value, given the property name string. There are - * several forms of property access functions, depending on the data type returned. - * + * String property name: identifies the maker of the DRM engine plugin + */ + public static final String PROPERTY_VENDOR = "vendor"; + + /** + * String property name: identifies the version of the DRM engine plugin + */ + public static final String PROPERTY_VERSION = "version"; + + /** + * String property name: describes the DRM engine plugin + */ + public static final String PROPERTY_DESCRIPTION = "description"; + + /** + * String property name: a comma-separated list of cipher and mac algorithms + * supported by CryptoSession. The list may be empty if the DRM engine + * plugin does not support CryptoSession operations. + */ + public static final String PROPERTY_ALGORITHM = "algorithm"; + + /** + * Read a DRM engine plugin String property value, given the property name string. + * <p> * Standard fields names are: - * vendor String - identifies the maker of the DRM engine plugin - * version String - identifies the version of the DRM engine plugin - * description String - describes the DRM engine plugin - * deviceUniqueId byte[] - The device unique identifier is established during device - * provisioning and provides a means of uniquely identifying - * each device - * algorithms String - a comma-separate list of cipher and mac algorithms supported - * by CryptoSession. The list may be empty if the DRM engine - * plugin does not support CryptoSession operations. + * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, + * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHM} */ - public native String getPropertyString(String propertyName) - throws MediaDrmException; + public native String getPropertyString(String propertyName); - public native byte[] getPropertyByteArray(String propertyName) - throws MediaDrmException; /** - * Write a DRM engine plugin property value. There are several forms of - * property setting functions, depending on the data type being set. + * The device unique identifier is established during device provisioning and + * provides a means of uniquely identifying each device */ - public native void setPropertyString(String propertyName, String value) - throws MediaDrmException; + public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; + + /** + * Read a DRM engine plugin byte array property value, given the property name string. + * <p> + * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} + */ + public native byte[] getPropertyByteArray(String propertyName); - public native void setPropertyByteArray(String propertyName, byte[] value) - throws MediaDrmException; /** - * In addition to supporting decryption of DASH Common Encrypted Media, the - * MediaDrm APIs provide the ability to securely deliver session keys from - * an operator's session key server to a client device, based on the factory-installed - * root of trust, and provide the ability to do encrypt, decrypt, sign and verify - * with the session key on arbitrary user data. - * - * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods - * based on the established session keys. These keys are exchanged using the - * getKeyRequest/provideKeyResponse methods. - * - * Applications of this capability could include securing various types of - * purchased or private content, such as applications, books and other media, - * photos or media delivery protocols. - * - * Operators can create session key servers that are functionally similar to a - * license key server, except that instead of receiving license key requests and - * providing encrypted content keys which are used specifically to decrypt A/V media - * content, the session key server receives session key requests and provides - * encrypted session keys which can be used for general purpose crypto operations. + * Set a DRM engine plugin String property value. + */ + public native void setPropertyString(String propertyName, String value); + + /** + * Set a DRM engine plugin byte array property value. */ + public native void setPropertyByteArray(String propertyName, byte[] value); + private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId, String algorithm); @@ -429,61 +536,112 @@ public final class MediaDrm { byte[] keyId, byte[] message, byte[] signature); + /** + * In addition to supporting decryption of DASH Common Encrypted Media, the + * MediaDrm APIs provide the ability to securely deliver session keys from + * an operator's session key server to a client device, based on the factory-installed + * root of trust, and then perform encrypt, decrypt, sign and verify operations + * with the session key on arbitrary user data. + * <p> + * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods + * based on the established session keys. These keys are exchanged using the + * getKeyRequest/provideKeyResponse methods. + * <p> + * Applications of this capability could include securing various types of + * purchased or private content, such as applications, books and other media, + * photos or media delivery protocols. + * <p> + * Operators can create session key servers that are functionally similar to a + * license key server, except that instead of receiving license key requests and + * providing encrypted content keys which are used specifically to decrypt A/V media + * content, the session key server receives session key requests and provides + * encrypted session keys which can be used for general purpose crypto operations. + * <p> + * A CryptoSession is obtained using {@link #getCryptoSession} + */ public final class CryptoSession { private MediaDrm mDrm; private byte[] mSessionId; - /** - * Construct a CryptoSession which can be used to encrypt, decrypt, - * sign and verify messages or data using the session keys established - * for the session using methods {@link getKeyRequest} and - * {@link provideKeyResponse} using a session key server. - * - * @param sessionId the session ID for the session containing keys - * to be used for encrypt, decrypt, sign and/or verify - * - * @param cipherAlgorithm the algorithm to use for encryption and - * decryption ciphers. The algorithm string conforms to JCA Standard - * Names for Cipher Transforms and is case insensitive. For example - * "AES/CBC/PKCS5Padding". - * - * @param macAlgorithm the algorithm to use for sign and verify - * The algorithm string conforms to JCA Standard Names for Mac - * Algorithms and is case insensitive. For example "HmacSHA256". - * - * The list of supported algorithms for a DRM engine plugin can be obtained - * using the method {@link getPropertyString("algorithms")} - */ - - public CryptoSession(MediaDrm drm, byte[] sessionId, - String cipherAlgorithm, String macAlgorithm) - throws MediaDrmException { + CryptoSession(MediaDrm drm, byte[] sessionId, + String cipherAlgorithm, String macAlgorithm) + { mSessionId = sessionId; mDrm = drm; setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm); setMacAlgorithmNative(drm, sessionId, macAlgorithm); } + /** + * Encrypt data using the CryptoSession's cipher algorithm + * + * @param keyid specifies which key to use + * @param input the data to encrypt + * @param iv the initialization vector to use for the cipher + */ public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) { return encryptNative(mDrm, mSessionId, keyid, input, iv); } + /** + * Decrypt data using the CryptoSessions's cipher algorithm + * + * @param keyid specifies which key to use + * @param input the data to encrypt + * @param iv the initialization vector to use for the cipher + */ public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) { return decryptNative(mDrm, mSessionId, keyid, input, iv); } + /** + * Sign data using the CryptoSessions's mac algorithm. + * + * @param keyid specifies which key to use + * @param message the data for which a signature is to be computed + */ public byte[] sign(byte[] keyid, byte[] message) { return signNative(mDrm, mSessionId, keyid, message); } + + /** + * Verify a signature using the CryptoSessions's mac algorithm. Return true + * if the signatures match, false if they do no. + * + * @param keyid specifies which key to use + * @param message the data to verify + * @param signature the reference signature which will be compared with the + * computed signature + */ public boolean verify(byte[] keyid, byte[] message, byte[] signature) { return verifyNative(mDrm, mSessionId, keyid, message, signature); } }; + /** + * Obtain a CryptoSession object which can be used to encrypt, decrypt, + * sign and verify messages or data using the session keys established + * for the session using methods {@link #getKeyRequest} and + * {@link #provideKeyResponse} using a session key server. + * + * @param sessionId the session ID for the session containing keys + * to be used for encrypt, decrypt, sign and/or verify + * @param cipherAlgorithm the algorithm to use for encryption and + * decryption ciphers. The algorithm string conforms to JCA Standard + * Names for Cipher Transforms and is case insensitive. For example + * "AES/CBC/NoPadding". + * @param macAlgorithm the algorithm to use for sign and verify + * The algorithm string conforms to JCA Standard Names for Mac + * Algorithms and is case insensitive. For example "HmacSHA256". + * <p> + * The list of supported algorithms for a DRM engine plugin can be obtained + * using the method {@link #getPropertyString} with the property name + * "algorithms". + */ public CryptoSession getCryptoSession(byte[] sessionId, String cipherAlgorithm, String macAlgorithm) - throws MediaDrmException { + { return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm); } @@ -495,8 +653,7 @@ public final class MediaDrm { public native final void release(); private static native final void native_init(); - private native final void native_setup(Object mediadrm_this, byte[] uuid) - throws MediaDrmException; + private native final void native_setup(Object mediadrm_this, byte[] uuid); private native final void native_finalize(); diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java index 6f81f90..d6f5ff4 100644 --- a/media/java/android/media/MediaDrmException.java +++ b/media/java/android/media/MediaDrmException.java @@ -19,8 +19,6 @@ package android.media; /** * Exception thrown if MediaDrm object could not be instantiated for * whatever reason. - * - * @hide -- don't expose yet */ public final class MediaDrmException extends Exception { public MediaDrmException(String detailMessage) { diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 4a5e82e..61a0134 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -999,7 +999,7 @@ public class RemoteControlClient if (mEventHandler != null) { // signal new client mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN); - mEventHandler.dispatchMessage( + mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN, /*arg1*/ generationId, /*arg2, ignored*/ 0)); // send the information @@ -1007,12 +1007,12 @@ public class RemoteControlClient mEventHandler.removeMessages(MSG_REQUEST_METADATA); mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL); mEventHandler.removeMessages(MSG_REQUEST_ARTWORK); - mEventHandler.dispatchMessage( + mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE)); - mEventHandler.dispatchMessage( + mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL)); - mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA)); - mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK)); + mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA)); + mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK)); } } @@ -1020,7 +1020,7 @@ public class RemoteControlClient // only post messages, we can't block here if (mEventHandler != null) { mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN); - mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + mEventHandler.sendMessage(mEventHandler.obtainMessage( MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/)); } } @@ -1028,7 +1028,7 @@ public class RemoteControlClient public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { // only post messages, we can't block here if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + mEventHandler.sendMessage(mEventHandler.obtainMessage( MSG_PLUG_DISPLAY, w, h, rcd)); } } @@ -1036,7 +1036,7 @@ public class RemoteControlClient public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) { // only post messages, we can't block here if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + mEventHandler.sendMessage(mEventHandler.obtainMessage( MSG_UNPLUG_DISPLAY, rcd)); } } @@ -1044,7 +1044,7 @@ public class RemoteControlClient public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) { // only post messages, we can't block here if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + mEventHandler.sendMessage(mEventHandler.obtainMessage( MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd)); } } @@ -1053,7 +1053,7 @@ public class RemoteControlClient // only post messages, we can't block here if (mEventHandler != null) { mEventHandler.removeMessages(MSG_SEEK_TO); - mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + mEventHandler.sendMessage(mEventHandler.obtainMessage( MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */, new Long(timeMs))); } @@ -1145,6 +1145,7 @@ public class RemoteControlClient break; case MSG_SEEK_TO: onSeekTo(msg.arg1, ((Long)msg.obj).longValue()); + break; default: Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler"); } diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index c32ba9d..d9d466e 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -458,22 +458,22 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { "(Ljava/lang/Object;IILjava/lang/Object;)V"); jfieldID field; - GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_PROVISION_REQUIRED", "I"); + GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I"); gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_REQUIRED", "I"); + GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I"); gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_EXPIRED", "I"); + GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I"); gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_VENDOR_DEFINED", "I"); + GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I"); gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field); FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); - GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B"); - GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); + GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B"); + GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); - GET_FIELD_ID(gFields.provisionRequest.data, clazz, "data", "[B"); - GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); + GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B"); + GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); FIND_CLASS(clazz, "java/util/ArrayList"); GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V"); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java index 9146ccd..2c25236 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java @@ -30,6 +30,7 @@ import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.IBinder; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -54,7 +55,10 @@ public class UsbDebuggingActivity extends AlertActivity public void onCreate(Bundle icicle) { super.onCreate(icicle); - mDisconnectedReceiver = new UsbDisconnectedReceiver(this); + if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) { + mDisconnectedReceiver = new UsbDisconnectedReceiver(this); + } + Intent intent = getIntent(); String fingerprints = intent.getStringExtra("fingerprints"); mKey = intent.getStringExtra("key"); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 7cb6b09..7f3fc43 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -251,8 +251,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidOpenRotation; int mCarDockRotation; int mDeskDockRotation; - int mHdmiRotation; - boolean mHdmiRotationLock; + int mUndockedHdmiRotation; + int mDemoHdmiRotation; + boolean mDemoHdmiRotationLock; int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; int mUserRotation = Surface.ROTATION_0; @@ -854,6 +855,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_carDockRotation); mDeskDockRotation = readRotation( com.android.internal.R.integer.config_deskDockRotation); + mUndockedHdmiRotation = readRotation( + com.android.internal.R.integer.config_undockedHdmiRotation); mCarDockEnablesAccelerometer = mContext.getResources().getBoolean( com.android.internal.R.bool.config_carDockEnablesAccelerometer); mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean( @@ -1024,11 +1027,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // For demo purposes, allow the rotation of the HDMI display to be controlled. // By default, HDMI locks rotation to landscape. if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { - mHdmiRotation = mPortraitRotation; + mDemoHdmiRotation = mPortraitRotation; } else { - mHdmiRotation = mLandscapeRotation; + mDemoHdmiRotation = mLandscapeRotation; } - mHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false); + mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false); } @Override @@ -4200,10 +4203,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { // enable 180 degree rotation while docked. preferredRotation = mDeskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation; - } else if (mHdmiPlugged && mHdmiRotationLock) { - // Ignore sensor when plugged into HDMI. + } else if (mHdmiPlugged && mDemoHdmiRotationLock) { + // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled. // Note that the dock orientation overrides the HDMI orientation. - preferredRotation = mHdmiRotation; + preferredRotation = mDemoHdmiRotation; + } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED + && mUndockedHdmiRotation >= 0) { + // Ignore sensor when plugged into HDMI and an undocked orientation has + // been specified in the configuration (only for legacy devices without + // full multi-display support). + // Note that the dock orientation overrides the HDMI orientation. + preferredRotation = mUndockedHdmiRotation; } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { // Application just wants to remain locked in the last rotation. preferredRotation = lastRotation; @@ -4967,7 +4977,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation); pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation); pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation); - pw.print(prefix); pw.print("mHdmiRotation="); pw.print(mHdmiRotation); - pw.print(" mHdmiRotationLock="); pw.println(mHdmiRotationLock); + pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation); + pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); + pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java index 77359ff..9b58803 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java @@ -23,12 +23,16 @@ import android.content.ContentResolver; import android.content.Context; import android.os.BatteryManager; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Slog; import android.view.View; import android.widget.TextView; @@ -37,6 +41,8 @@ import libcore.util.MutableInt; import java.lang.ref.WeakReference; import com.android.internal.R; +import com.android.internal.widget.ILockSettings; +import com.android.internal.widget.LockPatternUtils; /*** * Manages a number of views inside of the given layout. See below for a list of widgets. @@ -57,6 +63,8 @@ class KeyguardMessageArea extends TextView { static final int SECURITY_MESSAGE_DURATION = 5000; protected static final int FADE_DURATION = 750; + private static final String TAG = "KeyguardMessageArea"; + // are we showing battery information? boolean mShowingBatteryInfo = false; @@ -82,6 +90,9 @@ class KeyguardMessageArea extends TextView { CharSequence mMessage; boolean mShowingMessage; + private CharSequence mSeparator; + private LockPatternUtils mLockPatternUtils; + Runnable mClearMessageRunnable = new Runnable() { @Override public void run() { @@ -156,8 +167,6 @@ class KeyguardMessageArea extends TextView { } }; - private CharSequence mSeparator; - public KeyguardMessageArea(Context context) { this(context, null); } @@ -165,6 +174,8 @@ class KeyguardMessageArea extends TextView { public KeyguardMessageArea(Context context, AttributeSet attrs) { super(context, attrs); + mLockPatternUtils = new LockPatternUtils(context); + // This is required to ensure marquee works setSelected(true); @@ -228,11 +239,12 @@ class KeyguardMessageArea extends TextView { String getOwnerInfo() { ContentResolver res = getContext().getContentResolver(); - final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res, - Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0; - return ownerInfoEnabled && !mShowingMessage ? - Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO, - UserHandle.USER_CURRENT) : null; + String info = null; + final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(); + if (ownerInfoEnabled && !mShowingMessage) { + info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser()); + } + return info; } private CharSequence getChargeInfo(MutableInt icon) { diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index a28c387..f872cc3 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1595,7 +1595,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(), PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, mSettings.getCurrentUserId()); - if (ai.enabledSetting + if (ai != null && ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java index e20a21f..41cc4d7 100644 --- a/services/java/com/android/server/LockSettingsService.java +++ b/services/java/com/android/server/LockSettingsService.java @@ -19,6 +19,11 @@ package com.android.server; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; + +import static android.content.Context.USER_SERVICE; +import static android.Manifest.permission.READ_PROFILE; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -27,8 +32,10 @@ import android.os.Environment; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Secure; +import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Slog; @@ -40,6 +47,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Arrays; +import java.util.List; /** * Keeps the lock pattern/password data and related settings for each user. @@ -79,23 +87,52 @@ public class LockSettingsService extends ILockSettings.Stub { private void migrateOldData() { try { - if (getString("migrated", null, 0) != null) { - // Already migrated - return; + // These Settings moved before multi-user was enabled, so we only have to do it for the + // root user. + if (getString("migrated", null, 0) == null) { + final ContentResolver cr = mContext.getContentResolver(); + for (String validSetting : VALID_SETTINGS) { + String value = Settings.Secure.getString(cr, validSetting); + if (value != null) { + setString(validSetting, value, 0); + } + } + // No need to move the password / pattern files. They're already in the right place. + setString("migrated", "true", 0); + Slog.i(TAG, "Migrated lock settings to new location"); } - final ContentResolver cr = mContext.getContentResolver(); - for (String validSetting : VALID_SETTINGS) { - String value = Settings.Secure.getString(cr, validSetting); - if (value != null) { - setString(validSetting, value, 0); + // These Settings changed after multi-user was enabled, hence need to be moved per user. + if (getString("migrated_user_specific", null, 0) == null) { + final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); + final ContentResolver cr = mContext.getContentResolver(); + List<UserInfo> users = um.getUsers(); + for (int user = 0; user < users.size(); user++) { + int userId = users.get(user).getUserHandle().getIdentifier(); + for (String perUserSetting : MIGRATE_SETTINGS_PER_USER) { + // Handle Strings + String value = Settings.Secure.getStringForUser(cr, perUserSetting, userId); + if (value != null) { + setString(perUserSetting, value, userId); + Settings.Secure.putStringForUser(cr, perUserSetting, "", userId); + continue; + } + + // Handle integers + try { + int ivalue = Settings.Secure.getIntForUser(cr, perUserSetting, userId); + setLong(perUserSetting, ivalue, userId); + Settings.Secure.putIntForUser(cr, perUserSetting, 0, userId); + } catch (SettingNotFoundException e) { + } + } } + // No need to move the password / pattern files. They're already in the right place. + setString("migrated_user_specific", "true", 0); + Slog.i(TAG, "Migrated per-user lock settings to new location"); } - // No need to move the password / pattern files. They're already in the right place. - setString("migrated", "true", 0); - Slog.i(TAG, "Migrated lock settings to new location"); } catch (RemoteException re) { - Slog.e(TAG, "Unable to migrate old data"); + Slog.e(TAG, "Unable to migrate old data", re); } } @@ -115,12 +152,16 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private static final void checkReadPermission(int userId) { + private final void checkReadPermission(String requestedKey, int userId) { final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID - && UserHandle.getUserId(callingUid) != userId) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read settings of user " + userId); + for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) { + String key = READ_PROFILE_PROTECTED_SETTINGS[i]; + if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("uid=" + callingUid + + " needs permission " + READ_PROFILE + " to read " + + requestedKey + " for user " + userId); + } } } @@ -147,7 +188,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); + checkReadPermission(key, userId); String value = readFromDb(key, null, userId); return TextUtils.isEmpty(value) ? @@ -156,7 +197,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public long getLong(String key, long defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); + checkReadPermission(key, userId); String value = readFromDb(key, null, userId); return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); @@ -164,7 +205,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public String getString(String key, String defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); + checkReadPermission(key, userId); return readFromDb(key, defaultValue, userId); } @@ -404,5 +445,13 @@ public class LockSettingsService extends ILockSettings.Stub { Secure.LOCK_BIOMETRIC_WEAK_FLAGS, Secure.LOCK_PATTERN_VISIBLE, Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED - }; + }; + + private static final String[] MIGRATE_SETTINGS_PER_USER = new String[] { + Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, + Secure.LOCK_SCREEN_OWNER_INFO + }; + + // These are protected with a read permission + private static final String[] READ_PROFILE_PROTECTED_SETTINGS = MIGRATE_SETTINGS_PER_USER; } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index fa18e76..3bebf91 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -1676,8 +1676,12 @@ public class NotificationManagerService extends INotificationManager.Stub .getSystemService(Context.AUDIO_SERVICE); // sound + + // should we use the default notification sound? (indicated either by DEFAULT_SOUND + // or because notification.sound is pointing at Settings.System.NOTIFICATION_SOUND) final boolean useDefaultSound = - (notification.defaults & Notification.DEFAULT_SOUND) != 0; + (notification.defaults & Notification.DEFAULT_SOUND) != 0 + || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound); Uri soundUri = null; boolean hasValidSound = false; diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java index 0688c50..de439d7 100644 --- a/services/java/com/android/server/am/NativeCrashListener.java +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -43,13 +43,14 @@ import java.net.InetUnixAddress; class NativeCrashListener extends Thread { static final String TAG = "NativeCrashListener"; static final boolean DEBUG = false; + static final boolean MORE_DEBUG = DEBUG && false; // Must match the path defined in debuggerd.c. static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket"; // Use a short timeout on socket operations and abandon the connection // on hard errors - static final long SOCKET_TIMEOUT_MILLIS = 1000; // 1 second + static final long SOCKET_TIMEOUT_MILLIS = 2000; // 2 seconds final ActivityManagerService mAm; @@ -124,9 +125,9 @@ class NativeCrashListener extends Thread { InetSocketAddress peer = new InetSocketAddress(); FileDescriptor peerFd = null; try { - if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); + if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); peerFd = Libcore.os.accept(serverFd, peer); - if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); + if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); if (peerFd != null) { // Only the superuser is allowed to talk to us over this socket StructUcred credentials = @@ -145,7 +146,12 @@ class NativeCrashListener extends Thread { if (peerFd != null) { try { Libcore.os.write(peerFd, ackSignal, 0, 1); - } catch (Exception e) { /* we don't care about failures here */ } + } catch (Exception e) { + /* we don't care about failures here */ + if (MORE_DEBUG) { + Slog.d(TAG, "Exception writing ack: " + e.getMessage()); + } + } } } } @@ -183,7 +189,7 @@ class NativeCrashListener extends Thread { // Read the crash report from the debuggerd connection void consumeNativeCrashData(FileDescriptor fd) { - if (DEBUG) Slog.i(TAG, "debuggerd connected"); + if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected"); final byte[] buf = new byte[4096]; final ByteArrayOutputStream os = new ByteArrayOutputStream(4096); @@ -218,7 +224,7 @@ class NativeCrashListener extends Thread { // get some data bytes = Libcore.os.read(fd, buf, 0, buf.length); if (bytes > 0) { - if (DEBUG) { + if (MORE_DEBUG) { String s = new String(buf, 0, bytes, "UTF-8"); Slog.v(TAG, "READ=" + bytes + "> " + s); } diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index df90a56..11c6dab 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -88,8 +88,13 @@ public class UserManagerService extends IUserManager.Stub { private static final String TAG_ENTRY = "entry"; private static final String TAG_VALUE = "value"; private static final String ATTR_KEY = "key"; + private static final String ATTR_VALUE_TYPE = "type"; private static final String ATTR_MULTIPLE = "m"; + private static final String ATTR_TYPE_STRING_ARRAY = "sa"; + private static final String ATTR_TYPE_STRING = "s"; + private static final String ATTR_TYPE_BOOLEAN = "b"; + private static final String USER_INFO_DIR = "system" + File.separator + "users"; private static final String USER_LIST_FILENAME = "userlist.xml"; private static final String USER_PHOTO_FILENAME = "photo.png"; @@ -965,7 +970,12 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) { + public Bundle getApplicationRestrictions(String packageName) { + return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId()); + } + + @Override + public Bundle getApplicationRestrictionsForUser(String packageName, int userId) { if (UserHandle.getCallingUserId() != userId || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) { checkManageUsersPermission("Only system can get restrictions for other users/apps"); @@ -977,7 +987,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries, + public void setApplicationRestrictions(String packageName, Bundle restrictions, int userId) { if (UserHandle.getCallingUserId() != userId || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) { @@ -985,7 +995,7 @@ public class UserManagerService extends IUserManager.Stub { } synchronized (mPackagesLock) { // Write the restrictions to XML - writeApplicationRestrictionsLocked(packageName, entries, userId); + writeApplicationRestrictionsLocked(packageName, restrictions, userId); } } @@ -1001,9 +1011,9 @@ public class UserManagerService extends IUserManager.Stub { } } - private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName, + private Bundle readApplicationRestrictionsLocked(String packageName, int userId) { - final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>(); + final Bundle restrictions = new Bundle(); final ArrayList<String> values = new ArrayList<String>(); FileInputStream fis = null; @@ -1023,12 +1033,13 @@ public class UserManagerService extends IUserManager.Stub { if (type != XmlPullParser.START_TAG) { Slog.e(LOG_TAG, "Unable to read restrictions file " + restrictionsFile.getBaseFile()); - return entries; + return restrictions; } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) { String key = parser.getAttributeValue(null, ATTR_KEY); + String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE); String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE); if (multiple != null) { int count = Integer.parseInt(multiple); @@ -1041,14 +1052,13 @@ public class UserManagerService extends IUserManager.Stub { } String [] valueStrings = new String[values.size()]; values.toArray(valueStrings); - Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings); - RestrictionEntry entry = new RestrictionEntry(key, valueStrings); - entries.add(entry); + restrictions.putStringArray(key, valueStrings); + } else if (ATTR_TYPE_BOOLEAN.equals(valType)) { + restrictions.putBoolean(key, Boolean.parseBoolean( + parser.nextText().trim())); } else { String value = parser.nextText().trim(); - Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value); - RestrictionEntry entry = new RestrictionEntry(key, value); - entries.add(entry); + restrictions.putString(key, value); } } } @@ -1063,11 +1073,11 @@ public class UserManagerService extends IUserManager.Stub { } } } - return entries; + return restrictions; } private void writeApplicationRestrictionsLocked(String packageName, - List<RestrictionEntry> entries, int userId) { + Bundle restrictions, int userId) { FileOutputStream fos = null; AtomicFile restrictionsFile = new AtomicFile( new File(Environment.getUserSystemDirectory(userId), @@ -1084,18 +1094,24 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_RESTRICTIONS); - for (RestrictionEntry entry : entries) { + for (String key : restrictions.keySet()) { + Object value = restrictions.get(key); serializer.startTag(null, TAG_ENTRY); - serializer.attribute(null, ATTR_KEY, entry.getKey()); - if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) { - String value = entry.getSelectedString(); - serializer.text(value != null ? value : ""); + serializer.attribute(null, ATTR_KEY, key); + + if (value instanceof Boolean) { + serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN); + serializer.text(value.toString()); + } else if (value == null || value instanceof String) { + serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING); + serializer.text(value != null ? (String) value : ""); } else { - String[] values = entry.getAllSelectedStrings(); + serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY); + String[] values = (String[]) value; serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length)); - for (String value : values) { + for (String choice : values) { serializer.startTag(null, TAG_VALUE); - serializer.text(value != null ? value : ""); + serializer.text(choice != null ? choice : ""); serializer.endTag(null, TAG_VALUE); } } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 1d1fda5..bc442ce 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5490,6 +5490,7 @@ public class WindowManagerService extends IWindowManager.Stub ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix); matrix.postTranslate(-FloatMath.ceil(frame.left*scale), -FloatMath.ceil(frame.top*scale)); Canvas canvas = new Canvas(bm); + canvas.drawColor(0xFF000000); canvas.drawBitmap(rawss, matrix, null); canvas.setBitmap(null); diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml index 1cb8c37..b55e290 100644 --- a/tests/CanvasCompare/AndroidManifest.xml +++ b/tests/CanvasCompare/AndroidManifest.xml @@ -15,8 +15,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.hwuicompare" > - <!-- for perfhud --> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:label="@string/app_name" diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java index e0ff1dc..1ed4723 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java @@ -16,11 +16,20 @@ package com.android.test.hwuicompare; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.TreeSet; -import com.android.test.hwuicompare.R; +import org.json.JSONException; +import org.json.JSONObject; import android.os.Bundle; +import android.os.Environment; import android.os.Trace; import android.util.Log; import android.widget.ImageView; @@ -31,20 +40,31 @@ public class AutomaticActivity extends CompareActivity { private static final float ERROR_DISPLAY_THRESHOLD = 0.01f; protected static final boolean DRAW_BITMAPS = false; - private ImageView mSoftwareImageView = null; - private ImageView mHardwareImageView = null; + /** + * Threshold of error change required to consider a test regressed/improved + */ + private static final float ERROR_CHANGE_THRESHOLD = 0.001f; - private static final float[] ERROR_CUTOFFS = {0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f}; - private float[] mErrorRates = new float[ERROR_CUTOFFS.length]; + private static final float[] ERROR_CUTOFFS = { + 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f + }; + + private final float[] mErrorRates = new float[ERROR_CUTOFFS.length]; private float mTotalTests = 0; private float mTotalError = 0; + private int mTestsRegressed = 0; + private int mTestsImproved = 0; - public abstract static class TestCallback { + private ImageView mSoftwareImageView = null; + private ImageView mHardwareImageView = null; + + + public abstract static class FinalCallback { abstract void report(String name, float value); - void complete() {} + void complete() {}; } - private ArrayList<TestCallback> mTestCallbacks = new ArrayList<TestCallback>(); + private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>(); Runnable mRunnable = new Runnable() { @Override @@ -64,32 +84,11 @@ public class AutomaticActivity extends CompareActivity { float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap); Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); - if (error > ERROR_DISPLAY_THRESHOLD) { - String modname = ""; - for (String s : DisplayModifier.getLastAppliedModifications()) { - modname = modname.concat(s + "."); - } - Log.d(LOG_TAG, String.format("error for %s was %2.9f", modname, error)); - } - for (int i = 0; i < ERROR_CUTOFFS.length; i++) { - if (error <= ERROR_CUTOFFS[i]) break; - mErrorRates[i]++; - } - mTotalError += error; - mTotalTests++; + final String[] modifierNames = DisplayModifier.getLastAppliedModifications(); + handleError(modifierNames, error); if (DisplayModifier.step()) { - for (TestCallback c : mTestCallbacks) { - c.report("averageError", (mTotalError / mTotalTests)); - for (int i = 1; i < ERROR_CUTOFFS.length; i++) { - c.report(String.format("error over %1.3f", ERROR_CUTOFFS[i]), - mErrorRates[i]/mTotalTests); - } - c.complete(); - } - - Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show(); - finish(); + finishTest(); } else { mHardwareView.invalidate(); if (DRAW_BITMAPS) { @@ -116,11 +115,186 @@ public class AutomaticActivity extends CompareActivity { mHardwareImageView = (ImageView) findViewById(R.id.hardware_image_view); onCreateCommon(mRunnable); - mTestCallbacks.add(new TestCallback() { + beginTest(); + } + + private static class TestResult { + TestResult(String label, float error) { + mLabel = label; + mTotalError = error; + mCount = 1; + } + public void addInto(float error) { + mTotalError += error; + mCount++; + } + public float getAverage() { + return mTotalError / mCount; + } + final String mLabel; + float mTotalError; + int mCount; + } + + JSONObject mOutputJson = null; + JSONObject mInputJson = null; + final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>(); + final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>(); + final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>(); + final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>(); + private void beginTest() { + mFinalCallbacks.add(new FinalCallback() { + @Override void report(String name, float value) { Log.d(LOG_TAG, name + " " + value); }; }); + + File inputFile = new File(Environment.getExternalStorageDirectory(), + "CanvasCompareInput.json"); + if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) { + try { + FileInputStream inputStream = new FileInputStream(inputFile); + Log.d(LOG_TAG, "Parsing input file..."); + StringBuffer content = new StringBuffer((int)inputFile.length()); + byte[] buffer = new byte[1024]; + while (inputStream.read(buffer) != -1) { + content.append(new String(buffer)); + } + mInputJson = new JSONObject(content.toString()); + inputStream.close(); + Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries"); + } catch (JSONException e) { + Log.e(LOG_TAG, "error parsing input json", e); + } catch (IOException e) { + Log.e(LOG_TAG, "error reading input json from sd", e); + } + } + + mOutputJson = new JSONObject(); + } + + private static void logTestResultHash(String label, HashMap<String, TestResult> map) { + Log.d(LOG_TAG, "---------------"); + Log.d(LOG_TAG, label + ":"); + Log.d(LOG_TAG, "---------------"); + TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() { + @Override + public int compare(TestResult lhs, TestResult rhs) { + if (lhs == rhs) return 0; // don't need to worry about complex equality + + int cmp = Float.compare(lhs.getAverage(), rhs.getAverage()); + if (cmp != 0) { + return cmp; + } + return lhs.mLabel.compareTo(rhs.mLabel); + } + }); + + for (TestResult t : map.values()) { + set.add(t); + } + + for (TestResult t : set.descendingSet()) { + if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) { + Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel)); + } + } + Log.d(LOG_TAG, ""); + } + + private void finishTest() { + for (FinalCallback c : mFinalCallbacks) { + c.report("averageError", (mTotalError / mTotalTests)); + for (int i = 1; i < ERROR_CUTOFFS.length; i++) { + c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]), + mErrorRates[i]); + } + if (mInputJson != null) { + c.report("tests regressed", mTestsRegressed); + c.report("tests improved", mTestsImproved); + } + c.complete(); + } + + try { + if (mOutputJson != null) { + String outputString = mOutputJson.toString(4); + File outputFile = new File(Environment.getExternalStorageDirectory(), + "CanvasCompareOutput.json"); + FileOutputStream outputStream = new FileOutputStream(outputFile); + outputStream.write(outputString.getBytes()); + outputStream.close(); + Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries"); + } + } catch (JSONException e) { + Log.e(LOG_TAG, "error during JSON stringify", e); + } catch (IOException e) { + Log.e(LOG_TAG, "error storing JSON output on sd", e); + } + + logTestResultHash("Modifier change vs previous", mModifierDiffResults); + logTestResultHash("Invidual test change vs previous", mIndividualDiffResults); + logTestResultHash("Modifier average test results", mModifierResults); + logTestResultHash("Individual test results", mIndividualResults); + + Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show(); + finish(); + } + + /** + * Inserts the error value into all TestResult objects, associated with each of its modifiers + */ + private static void addForAllModifiers(String fullName, float error, String[] modifierNames, + HashMap<String, TestResult> modifierResults) { + for (String modifierName : modifierNames) { + TestResult r = modifierResults.get(fullName); + if (r == null) { + modifierResults.put(modifierName, new TestResult(modifierName, error)); + } else { + r.addInto(error); + } + } + } + + private void handleError(final String[] modifierNames, final float error) { + String fullName = ""; + for (String s : modifierNames) { + fullName = fullName.concat("." + s); + } + fullName = fullName.substring(1); + + float deltaError = 0; + if (mInputJson != null) { + try { + deltaError = error - (float)mInputJson.getDouble(fullName); + } catch (JSONException e) { + Log.w(LOG_TAG, "Warning: unable to read from input json", e); + } + if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++; + if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++; + mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError)); + addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults); + } + + mIndividualResults.put(fullName, new TestResult(fullName, error)); + addForAllModifiers(fullName, error, modifierNames, mModifierResults); + + try { + if (mOutputJson != null) { + mOutputJson.put(fullName, error); + } + } catch (JSONException e) { + Log.e(LOG_TAG, "exception during JSON recording", e); + mOutputJson = null; + } + + for (int i = 0; i < ERROR_CUTOFFS.length; i++) { + if (error <= ERROR_CUTOFFS[i]) break; + mErrorRates[i]++; + } + mTotalError += error; + mTotalTests++; } @Override @@ -130,7 +304,7 @@ public class AutomaticActivity extends CompareActivity { } // FOR TESTING - public void setCallback(TestCallback c) { - mTestCallbacks.add(c); + public void setFinalCallback(FinalCallback c) { + mFinalCallbacks.add(c); } } diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java index 6ea8237..1ff153c 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java @@ -1,6 +1,6 @@ package com.android.test.hwuicompare; -import com.android.test.hwuicompare.AutomaticActivity.TestCallback; +import com.android.test.hwuicompare.AutomaticActivity.FinalCallback; import android.os.Bundle; import android.test.ActivityInstrumentationTestCase2; @@ -18,7 +18,7 @@ public class Test extends ActivityInstrumentationTestCase2<AutomaticActivity> { super.setUp(); mBundle = new Bundle(); mActivity = getActivity(); - mActivity.setCallback(new TestCallback() { + mActivity.setFinalCallback(new FinalCallback() { @Override void report(String key, float value) { |