Merge "Fix missing clock in keyguard" into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index e70f9f3..5bf4d41 100644
--- a/Android.mk
+++ b/Android.mk
@@ -124,6 +124,8 @@
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/input/IInputManager.aidl \
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/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
@@ -207,10 +209,12 @@
location/java/android/location/ICountryDetector.aidl \
location/java/android/location/ICountryListener.aidl \
location/java/android/location/IGeocodeProvider.aidl \
+ location/java/android/location/IGeofenceProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
+ location/java/android/location/IGpsGeofenceHardware.aidl \
location/java/android/location/INetInitiatedListener.aidl \
location/java/com/android/internal/location/ILocationProvider.aidl \
media/java/android/media/IAudioService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 1e1f01c..7451a93 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -69,6 +69,7 @@
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INTERNET = "android.permission.INTERNET";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+ field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -10443,6 +10444,43 @@
}
+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 pauseGeofence(int, int);
+ method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ method public boolean removeGeofence(int, int);
+ method public boolean resumeGeofence(int, int, int);
+ method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ 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
+ field public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4; // 0x4
+ field public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1; // 0x1
+ field public static final int GEOFENCE_EXITED = 2; // 0x2
+ field public static final int GEOFENCE_FAILURE = 5; // 0x5
+ field public static final int GEOFENCE_SUCCESS = 0; // 0x0
+ field public static final int GEOFENCE_UNCERTAIN = 4; // 0x4
+ field public static final int MONITORING_TYPE_GPS_HARDWARE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_AVAILABLE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1; // 0x1
+ field public static final int MONITOR_UNSUPPORTED = 2; // 0x2
+ }
+
+ 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 onMonitoringSystemChange(int, boolean, android.location.Location);
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
@@ -16835,7 +16873,6 @@
method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final deprecated boolean supportsProcesses();
- field public static final int BLUETOOTH_GID = 2000; // 0x7d0
field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
field public static final int PHONE_UID = 1001; // 0x3e9
@@ -18442,6 +18479,7 @@
}
protected static abstract interface ContactsContract.ContactsColumns {
+ field public static final java.lang.String CONTACT_LAST_UPDATED_TIMESTAMP = "contact_last_updated_timestamp";
field public static final java.lang.String DISPLAY_NAME = "display_name";
field public static final java.lang.String HAS_PHONE_NUMBER = "has_phone_number";
field public static final java.lang.String IN_VISIBLE_GROUP = "in_visible_group";
@@ -18506,6 +18544,16 @@
field public static final java.lang.String TIMES_USED = "times_used";
}
+ public static final class ContactsContract.DeletedContacts implements android.provider.ContactsContract.DeletedContactsColumns {
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final long DAYS_KEPT_MILLISECONDS = 2592000000L; // 0x9a7ec800L
+ }
+
+ protected static abstract interface ContactsContract.DeletedContactsColumns {
+ field public static final java.lang.String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ field public static final java.lang.String CONTACT_ID = "contact_id";
+ }
+
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
@@ -18585,6 +18633,7 @@
public static final class ContactsContract.Intents {
ctor public ContactsContract.Intents();
field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
+ field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
@@ -19649,8 +19698,7 @@
method public int getUsage();
method public void ioReceive();
method public void ioSend();
- method public synchronized void resize(int);
- method public synchronized void resize(int, int);
+ method public deprecated synchronized void resize(int);
method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
method public void setSurface(android.view.Surface);
@@ -27589,8 +27637,6 @@
method public synchronized int getMinimumFontSize();
method public synchronized int getMinimumLogicalFontSize();
method public deprecated synchronized android.webkit.WebSettings.PluginState getPluginState();
- method public deprecated synchronized boolean getPluginsEnabled();
- method public deprecated synchronized java.lang.String getPluginsPath();
method public synchronized java.lang.String getSansSerifFontFamily();
method public boolean getSaveFormData();
method public deprecated boolean getSavePassword();
@@ -27636,8 +27682,6 @@
method public synchronized void setMinimumLogicalFontSize(int);
method public void setNeedInitialFocus(boolean);
method public deprecated synchronized void setPluginState(android.webkit.WebSettings.PluginState);
- method public deprecated synchronized void setPluginsEnabled(boolean);
- method public deprecated synchronized void setPluginsPath(java.lang.String);
method public deprecated synchronized void setRenderPriority(android.webkit.WebSettings.RenderPriority);
method public synchronized void setSansSerifFontFamily(java.lang.String);
method public void setSaveFormData(boolean);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b39c335..b9afe40 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
@@ -27,6 +28,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 8c46b21..d5ff84e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -9,6 +9,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libbinder \
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index cdbc405..e43501c 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -75,11 +75,14 @@
Float.parseFloat(args[3]), Float.parseFloat(args[4]), -1);
return;
}
- } else if (command.equals("touchscreen") || command.equals("touchpad")) {
+ } else if (command.equals("touchscreen") || command.equals("touchpad")
+ || command.equals("touchnavigation")) {
// determine input source
int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
if (command.equals("touchpad")) {
inputSource = InputDevice.SOURCE_TOUCHPAD;
+ } else if (command.equals("touchnavigation")) {
+ inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION;
}
// determine subcommand
if (args.length > 1) {
@@ -247,8 +250,9 @@
System.err.println("usage: input ...");
System.err.println(" input text <string>");
System.err.println(" input keyevent <key code number or name>");
- System.err.println(" input [touchscreen|touchpad] tap <x> <y>");
- System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2> [duration(ms)]");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] swipe "
+ + "<x1> <y1> <x2> <y2> [duration(ms)]");
System.err.println(" input trackball press");
System.err.println(" input trackball roll <dx> <dy>");
}
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
index ad537977..3083e31 100644
--- a/cmds/system_server/Android.mk
+++ b/cmds/system_server/Android.mk
@@ -7,7 +7,8 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
- libsystem_server
+ libsystem_server \
+ liblog
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)
@@ -17,4 +18,3 @@
include $(BUILD_EXECUTABLE)
include $(LOCAL_PATH)/library/Android.mk
-
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index c42424c..d78474e 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -16,10 +16,11 @@
libandroid_runtime \
libsensorservice \
libsurfaceflinger \
- libinput \
+ libinput \
libutils \
libbinder \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE:= libsystem_server
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 68a2397..e6ce963 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4322,6 +4322,10 @@
GLUtils.setTracingLevel(1);
}
+ // Allow application-generated systrace messages if we're debuggable.
+ boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ Trace.setAppTracingAllowed(appTracingAllowed);
+
/**
* Initialize the default http proxy in this process for the reasons we set the time zone.
*/
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
new file mode 100644
index 0000000..35bbb9c
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -0,0 +1,439 @@
+/*
+ * 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.content.Context;
+import android.location.Location;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This class handles geofences managed by various hardware subsystems. It contains
+ * the public APIs that is needed to accomplish the task.
+ *
+ * <p>The APIs should not be called directly by the app developers. A higher level api
+ * 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.
+ *
+ * <p> There are 3 states associated with a Geofence: Inside, Outside, Unknown.
+ * There are 3 transitions: {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
+ * {@link #GEOFENCE_UNCERTAIN}. The APIs only expose the transitions.
+ *
+ * <p> Inside state: The hardware subsystem is reasonably confident that the user is inside
+ * the geofence. Outside state: The hardware subsystem is reasonably confident that the user
+ * is outside the geofence Unknown state: Unknown state can be interpreted as a state in which the
+ * monitoring subsystem isn't confident enough that the user is either inside or
+ * outside the Geofence. If the accuracy does not improve for a sufficient period of time,
+ * the {@link #GEOFENCE_UNCERTAIN} transition would be triggered. If the accuracy improves later,
+ * an appropriate transition would be triggered. The "reasonably confident" parameter
+ * depends on the hardware system and the positioning algorithms used.
+ * For instance, {@link #MONITORING_TYPE_GPS_HARDWARE} uses 95% as a confidence level.
+ */
+public final class GeofenceHardware {
+ private IGeofenceHardware mService;
+
+ // Hardware systems that do geofence monitoring.
+ static final int NUM_MONITORS = 1;
+
+ /**
+ * Constant for geofence monitoring done by the GPS hardware.
+ */
+ public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * available for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_AVAILABLE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * unavailable for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1;
+
+ /**
+ * Constant to indiciate that the monitoring system is unsupported
+ * for hardware geofence monitoring.
+ */
+ public static final int MONITOR_UNSUPPORTED = 2;
+
+ // The following constants need to match geofence flags in gps.h
+ /**
+ * The constant to indicate that the user has entered the geofence.
+ */
+ public static final int GEOFENCE_ENTERED = 1<<0L;
+
+ /**
+ * The constant to indicate that the user has exited the geofence.
+ */
+ public static final int GEOFENCE_EXITED = 1<<1L;
+
+ /**
+ * The constant to indicate that the user is uncertain with respect to a
+ * geofence. nn
+ */
+ public static final int GEOFENCE_UNCERTAIN = 1<<2L;
+
+ /**
+ * The constant used to indicate success of the particular geofence call
+ */
+ public static final int GEOFENCE_SUCCESS = 0;
+
+ /**
+ * The constant used to indicate that too many geofences have been registered.
+ */
+ public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1;
+
+ /**
+ * The constant used to indicate that the geofence id already exists.
+ */
+ public static final int GEOFENCE_ERROR_ID_EXISTS = 2;
+
+ /**
+ * The constant used to indicate that the geofence id is unknown.
+ */
+ public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3;
+
+ /**
+ * The constant used to indicate that the transition requested for the geofence is invalid.
+ */
+ public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4;
+
+ /**
+ * The constant used to indicate that the geofence operation has failed.
+ */
+ public static final int GEOFENCE_FAILURE = 5;
+
+ static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
+ static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+
+ private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
+ mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
+ /**
+ * @hide
+ */
+ public GeofenceHardware(IGeofenceHardware service) {
+ mService = service;
+ }
+
+ /**
+ * Returns all the hardware geofence monitoring systems and their status.
+ * 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
+ * for monitoring geofences in certain scenarios. For example, when a user
+ * enters a building, the GPS hardware subsystem might not be able monitor
+ * 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.
+ */
+ public int[] getMonitoringTypesAndStatus() {
+ try {
+ return mService.getMonitoringTypesAndStatus();
+ } catch (RemoteException e) {
+ }
+ return new int[0];
+ }
+
+ /**
+ * Creates a circular geofence which is monitored by subsystems in the hardware.
+ *
+ * <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
+ * <ul>
+ * <li> The geofence Id
+ * <li> The location object indicating the last known location.
+ * <li> The transition associated with the geofence. One of
+ * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * <li> The timestamp when the geofence transition occured.
+ * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
+ * that was used.
+ * </ul>
+ *
+ * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter.
+ * The application does not need to hold a wakelock when the monitoring
+ * is being done by the underlying hardware subsystem. If the same geofence Id is being
+ * monitored by two different monitoring systems, the same id can be used for both calls, as
+ * long as the same callback object is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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.
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
+ * transition.
+ * @return true on success.
+ */
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, GeofenceHardwareCallback callback) {
+ try {
+ return mService.addCircularFence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsivenes, unknownTimer,
+ monitoringType, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Removes a geofence added by {@link #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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 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.
+ */
+ public boolean removeGeofence(int geofenceId, int monitoringType) {
+ try {
+ return mService.removeGeofence(geofenceId, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Pauses the monitoring of a geofence added by {@link #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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 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.
+ */
+ public boolean pauseGeofence(int geofenceId, int monitoringType) {
+ try {
+ return mService.pauseGeofence(geofenceId, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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 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.
+ */
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ try {
+ return mService.resumeGeofence(geofenceId, monitorTransition, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Register the callback to be notified when the state of a hardware geofence
+ * monitoring system changes. For instance, it can change from
+ * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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.
+ *
+ * <p> The same callback object can be used to be informed of geofence transitions
+ * and state changes of the underlying hardware subsystem.
+ *
+ * @param monitoringType Type of the monitor
+ * @param callback Callback that will be called.
+ * @return true on success
+ */
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ GeofenceHardwareCallback callback) {
+ try {
+ return mService.registerForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
+ * to notify when the state of the hardware geofence monitoring system changes.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * 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 monitoringType Type of the monitor
+ * @param callback Callback that will be called.
+ * @return true on success
+ */
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ GeofenceHardwareCallback callback) {
+ boolean result = false;
+ try {
+ result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(callback));
+ if (result) removeCallback(callback);
+
+ } catch (RemoteException e) {
+ }
+ return result;
+ }
+
+
+ private void removeCallback(GeofenceHardwareCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
+ synchronized (mCallbacks) {
+ GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
+ if (wrapper == null) {
+ wrapper = new GeofenceHardwareCallbackWrapper(callback);
+ mCallbacks.put(callback, wrapper);
+ }
+ return wrapper;
+ }
+ }
+
+ class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
+ private WeakReference<GeofenceHardwareCallback> mCallback;
+
+ GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
+ mCallback = new WeakReference<GeofenceHardwareCallback>(c);
+ }
+
+ public void onMonitoringSystemChange(int monitoringType, boolean available,
+ Location location) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onMonitoringSystemChange(monitoringType, available, location);
+ }
+
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceChange(geofenceId, transition, location, timestamp,
+ monitoringType);
+ }
+ }
+
+ public void onGeofenceAdd(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceAdd(geofenceId, status);
+ }
+
+ public void onGeofenceRemove(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceRemove(geofenceId, status);
+ removeCallback(c);
+ }
+ }
+
+ public void onGeofencePause(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofencePause(geofenceId, status);
+ }
+
+ public void onGeofenceResume(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceResume(geofenceId, status);
+ }
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareCallback.java b/core/java/android/hardware/location/GeofenceHardwareCallback.java
new file mode 100644
index 0000000..8ab582a
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareCallback.java
@@ -0,0 +1,100 @@
+/*
+ * 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 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.
+ *
+ * @param geofenceId The geofence ID of the geofence
+ * @param transition One of {@link GeofenceHardware#GEOFENCE_ENTERED},
+ * {@link GeofenceHardware#GEOFENCE_EXITED}, {@link GeofenceHardware#GEOFENCE_UNCERTAIN}
+ * @param location The last known location according to the monitoring system.
+ * @param timestamp The timestamp (elapsed real time in milliseconds) when the transition was
+ * detected
+ * @param monitoringType Type of the monitoring system.
+ */
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the add call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_EXISTS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_TOO_MANY_GEOFENCES},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceAdd(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the remove call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceRemove(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the pause call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofencePause(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the resume call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceResume(int geofenceId, int status) {
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
new file mode 100644
index 0000000..21f1ea6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -0,0 +1,599 @@
+/*
+ * 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.content.Context;
+import android.content.pm.PackageManager;
+import android.location.IGpsGeofenceHardware;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class manages the geofences which are handled by hardware.
+ *
+ * @hide
+ */
+public final class GeofenceHardwareImpl {
+ private static final String TAG = "GeofenceHardwareImpl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private static GeofenceHardwareImpl sInstance;
+ private PowerManager.WakeLock mWakeLock;
+ private SparseArray<IGeofenceHardwareCallback> mGeofences =
+ new SparseArray<IGeofenceHardwareCallback>();
+ private ArrayList<IGeofenceHardwareCallback>[] mCallbacks =
+ new ArrayList[GeofenceHardware.NUM_MONITORS];
+
+ private IGpsGeofenceHardware mGpsService;
+
+ private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
+
+ // mGeofenceHandler message types
+ private static final int GEOFENCE_TRANSITION_CALLBACK = 1;
+ private static final int ADD_GEOFENCE_CALLBACK = 2;
+ private static final int REMOVE_GEOFENCE_CALLBACK = 3;
+ private static final int PAUSE_GEOFENCE_CALLBACK = 4;
+ private static final int RESUME_GEOFENCE_CALLBACK = 5;
+ private static final int ADD_GEOFENCE = 6;
+ private static final int REMOVE_GEOFENCE = 7;
+
+ // 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;
+
+ // The following constants need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+ // Resolution level constants used for permission checks.
+ // These constants must be in increasing order of finer resolution.
+ private static final int RESOLUTION_LEVEL_NONE = 1;
+ private static final int RESOLUTION_LEVEL_COARSE = 2;
+ private static final int RESOLUTION_LEVEL_FINE = 3;
+
+ // GPS Geofence errors. Should match gps.h constants.
+ private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
+ private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
+ private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
+ private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
+ private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
+ private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
+
+
+
+ public synchronized static GeofenceHardwareImpl getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new GeofenceHardwareImpl(context);
+ }
+ return sInstance;
+ }
+
+ private GeofenceHardwareImpl(Context context) {
+ mContext = context;
+ // Init everything to unsupported.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_UNSUPPORTED);
+
+ }
+
+ private void acquireWakeLock() {
+ if (mWakeLock == null) {
+ PowerManager powerManager =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+ mWakeLock.acquire();
+ }
+
+ private void releaseWakeLock() {
+ if (mWakeLock.isHeld()) mWakeLock.release();
+ }
+
+ private void updateGpsHardwareAvailability() {
+ //Check which monitors are available.
+ boolean gpsSupported;
+ try {
+ gpsSupported = mGpsService.isHardwareGeofenceSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception calling LocationManagerService");
+ gpsSupported = false;
+ }
+
+ if (gpsSupported) {
+ // Its assumed currently available at startup.
+ // native layer will update later.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ }
+ }
+
+ public void setGpsHardwareGeofence(IGpsGeofenceHardware service) {
+ if (mGpsService == null) {
+ mGpsService = service;
+ updateGpsHardwareAvailability();
+ } else if (service == null) {
+ mGpsService = null;
+ Log.w(TAG, "GPS Geofence Hardware service seems to have crashed");
+ } else {
+ Log.e(TAG, "Error: GpsService being set again.");
+ }
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ synchronized (mSupportedMonitorTypes) {
+ return mSupportedMonitorTypes;
+ }
+ }
+
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) {
+ Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + "Latitude: " + latitude +
+ "Longitude: " + longitude + "Radius: " + radius + "LastTransition: "
+ + lastTransition + "MonitorTransition: " + monitorTransitions +
+ "NotificationResponsiveness: " + notificationResponsivenes +
+ "UnKnown Timer: " + unknownTimer + "MonitoringType: " + monitoringType);
+
+ }
+ boolean result;
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE, callback);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude,
+ longitude, radius, lastTransition, monitorTransitions,
+ notificationResponsivenes, unknownTimer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (!result) {
+ m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result);
+ return result;
+ }
+
+ public boolean removeGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId);
+ boolean result = false;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.removeHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean pauseGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.pauseHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result);
+ return result;
+ }
+
+
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ // 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);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ private Location getLocation(int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude);
+ Location location = new Location(LocationManager.GPS_PROVIDER);
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+ location.setTime(timestamp);
+ // It would be nice to push the elapsed real-time timestamp
+ // further down the stack, but this is still useful
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ location.setAltitude(altitude);
+ } else {
+ location.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ location.setSpeed(speed);
+ } else {
+ location.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ location.setBearing(bearing);
+ } else {
+ location.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ location.setAccuracy(accuracy);
+ } else {
+ location.removeAccuracy();
+ }
+ return location;
+ }
+
+ /**
+ * called from GpsLocationProvider to report geofence transition
+ */
+ public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude +
+ " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " +
+ bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " +
+ transition + " TransitionTimestamp: " + transitionTimestamp);
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider to report GPS status change.
+ */
+ public void reportGpsGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ boolean available = false;
+ if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true;
+
+ int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE :
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val);
+
+ acquireWakeLock();
+ Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location);
+ m.arg1 = val;
+ mCallbacksHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider add geofence callback.
+ */
+ public void reportGpsGeofenceAddStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider remove geofence callback.
+ */
+ public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider pause geofence callback.
+ */
+ public void reportGpsGeofencePauseStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider resume geofence callback.
+ */
+ public void reportGpsGeofenceResumeStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ // All operations on mGeofences
+ private Handler mGeofenceHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int geofenceId;
+ int status;
+ IGeofenceHardwareCallback callback;
+ switch (msg.what) {
+ case ADD_GEOFENCE:
+ geofenceId = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ mGeofences.put(geofenceId, callback);
+ break;
+ case REMOVE_GEOFENCE:
+ geofenceId = msg.arg1;
+ mGeofences.remove(geofenceId);
+ break;
+ case ADD_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceAdd(geofenceId, msg.arg2);
+ } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
+ releaseWakeLock();
+ break;
+ case REMOVE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceRemove(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ mGeofences.remove(geofenceId);
+ releaseWakeLock();
+ break;
+
+ case PAUSE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofencePause(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case RESUME_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceResume(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case GEOFENCE_TRANSITION_CALLBACK:
+ GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj);
+ callback = mGeofences.get(geofenceTransition.mGeofenceId);
+
+ if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
+ geofenceTransition.mGeofenceId +
+ "Transition: " + geofenceTransition.mTransition +
+ "Location: " + geofenceTransition.mLocation + ":" + mGeofences);
+
+ try {
+ callback.onGeofenceChange(
+ geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
+ geofenceTransition.mLocation, geofenceTransition.mTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+ }
+ }
+ };
+
+ // All operations on mCallbacks
+ private Handler mCallbacksHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int monitoringType;
+ ArrayList<IGeofenceHardwareCallback> callbackList;
+ IGeofenceHardwareCallback callback;
+
+ switch (msg.what) {
+ case GPS_GEOFENCE_STATUS:
+ Location location = (Location) msg.obj;
+ int val = msg.arg1;
+ boolean available;
+ available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
+ true : false);
+ callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
+ if (callbackList == null) return;
+
+ if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
+
+ for (IGeofenceHardwareCallback c: callbackList) {
+ try {
+ c.onMonitoringSystemChange(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
+ location);
+ } catch (RemoteException e) {}
+ }
+ releaseWakeLock();
+ break;
+ case CALLBACK_ADD:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList == null) {
+ callbackList = new ArrayList<IGeofenceHardwareCallback>();
+ mCallbacks[monitoringType] = callbackList;
+ }
+ if (!callbackList.contains(callback)) callbackList.add(callback);
+ break;
+ case CALLBACK_REMOVE:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList != null) {
+ callbackList.remove(callback);
+ }
+ break;
+ }
+ }
+ };
+
+ private class GeofenceTransition {
+ private int mGeofenceId, mTransition;
+ private long mTimestamp;
+ private Location mLocation;
+
+ GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) {
+ mGeofenceId = geofenceId;
+ mTransition = transition;
+ mTimestamp = timestamp;
+ mLocation = location;
+ }
+ }
+
+ private void setMonitorAvailability(int monitor, int val) {
+ synchronized (mSupportedMonitorTypes) {
+ mSupportedMonitorTypes[monitor] = val;
+ }
+ }
+
+
+ int getMonitoringResolutionLevel(int monitoringType) {
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ return RESOLUTION_LEVEL_FINE;
+ }
+ return RESOLUTION_LEVEL_NONE;
+ }
+
+ int getAllowedResolutionLevel(int pid, int uid) {
+ if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_FINE;
+ } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_COARSE;
+ } else {
+ return RESOLUTION_LEVEL_NONE;
+ }
+ }
+
+ private int getGeofenceStatus(int status) {
+ switch (status) {
+ case GPS_GEOFENCE_OPERATION_SUCCESS:
+ return GeofenceHardware.GEOFENCE_SUCCESS;
+ case GPS_GEOFENCE_ERROR_GENERIC:
+ return GeofenceHardware.GEOFENCE_FAILURE;
+ case GPS_GEOFENCE_ERROR_ID_EXISTS:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+ case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
+ return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+ case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
+ return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+ case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+ }
+ return -1;
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
new file mode 100644
index 0000000..0eccee6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -0,0 +1,134 @@
+/*
+ * 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.Manifest;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.IGpsGeofenceHardware;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Service that handles hardware geofencing.
+ *
+ * @hide
+ */
+public class GeofenceHardwareService extends Service {
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+ private Context mContext;
+
+ @Override
+ public void onCreate() {
+ mContext = this;
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ mGeofenceHardwareImpl = null;
+ }
+
+
+ private void checkPermission(int pid, int uid, int monitoringType) {
+ if (mGeofenceHardwareImpl.getAllowedResolutionLevel(pid, uid) <
+ mGeofenceHardwareImpl.getMonitoringResolutionLevel(monitoringType)) {
+ throw new SecurityException("Insufficient permissions to access hardware geofence for"
+ + " type: " + monitoringType);
+ }
+ }
+
+ private IBinder mBinder = new IGeofenceHardware.Stub() {
+ public void setGpsGeofenceHardware(IGpsGeofenceHardware service) {
+ mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ return mGeofenceHardwareImpl.getMonitoringTypesAndStatus();
+ }
+
+ public boolean addCircularFence(int id, double lat, double longitude, double radius,
+ int lastTransition, int monitorTransitions, int
+ notificationResponsiveness, int unknownTimer, int monitoringType,
+ 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);
+ }
+
+ public boolean removeGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.removeGeofence(id, monitoringType);
+ }
+
+ public boolean pauseGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType);
+ }
+
+ public boolean resumeGeofence(int id, int monitorTransitions, int monitoringType) {
+ 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);
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ 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.registerForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ 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.unregisterForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+ };
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
new file mode 100644
index 0000000..4ba02b8
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -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/LICENS E-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.IGpsGeofenceHardware;
+import android.hardware.location.IGeofenceHardwareCallback;
+
+/** @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);
+ boolean removeGeofence(int id, int monitoringType);
+ boolean pauseGeofence(int id, int monitoringType);
+ boolean resumeGeofence(int id, int monitorTransitions, int monitoringType);
+ boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+ boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
new file mode 100644
index 0000000..678fc49
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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 IGeofenceHardwareCallback {
+ void onMonitoringSystemChange(int monitoringType, boolean available, in Location location);
+ void onGeofenceChange(int geofenceId, int transition, in Location location,
+ long timestamp, int monitoringType);
+ void onGeofenceAdd(int geofenceId, int status);
+ void onGeofenceRemove(int geofenceId, int status);
+ void onGeofencePause(int geofenceId, int status);
+ void onGeofenceResume(int geofenceId, int status);
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index facab4c..476b4ea 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -166,11 +166,6 @@
public static final int LAST_SHARED_APPLICATION_GID = 59999;
/**
- * Defines a secondary group id for access to the bluetooth hardware.
- */
- public static final int BLUETOOTH_GID = 2000;
-
- /**
* Standard priority of application threads.
* Use with {@link #setThreadPriority(int)} and
* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 27ed6b6..310b12c 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -19,41 +19,55 @@
import android.util.Log;
/**
- * Writes trace events to the kernel trace buffer. These trace events can be
- * collected using the "atrace" program for offline analysis.
+ * Writes trace events to the system trace buffer. These trace events can be
+ * collected and visualized using the Systrace tool.
*
* This tracing mechanism is independent of the method tracing mechanism
* offered by {@link Debug#startMethodTracing}. In particular, it enables
- * tracing of events that occur across processes.
+ * tracing of events that occur across multiple processes.
*
* @hide
*/
public final class Trace {
+ /*
+ * Writes trace events to the kernel trace buffer. These trace events can be
+ * collected using the "atrace" program for offline analysis.
+ */
+
private static final String TAG = "Trace";
// These tags must be kept in sync with system/core/include/cutils/trace.h.
+ /** @hide */
public static final long TRACE_TAG_NEVER = 0;
+ /** @hide */
public static final long TRACE_TAG_ALWAYS = 1L << 0;
+ /** @hide */
public static final long TRACE_TAG_GRAPHICS = 1L << 1;
+ /** @hide */
public static final long TRACE_TAG_INPUT = 1L << 2;
+ /** @hide */
public static final long TRACE_TAG_VIEW = 1L << 3;
+ /** @hide */
public static final long TRACE_TAG_WEBVIEW = 1L << 4;
+ /** @hide */
public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5;
+ /** @hide */
public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6;
+ /** @hide */
public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7;
+ /** @hide */
public static final long TRACE_TAG_AUDIO = 1L << 8;
+ /** @hide */
public static final long TRACE_TAG_VIDEO = 1L << 9;
+ /** @hide */
public static final long TRACE_TAG_CAMERA = 1L << 10;
+ /** @hide */
public static final long TRACE_TAG_HAL = 1L << 11;
+ /** @hide */
+ public static final long TRACE_TAG_APP = 1L << 12;
+
private static final long TRACE_TAG_NOT_READY = 1L << 63;
-
- public static final int TRACE_FLAGS_START_BIT = 1;
- public static final String[] TRACE_TAGS = {
- "Graphics", "Input", "View", "WebView", "Window Manager",
- "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", "HAL",
- };
-
- public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
+ private static final int MAX_SECTION_NAME_LEN = 127;
// Must be volatile to avoid word tearing.
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
@@ -62,6 +76,7 @@
private static native void nativeTraceCounter(long tag, String name, int value);
private static native void nativeTraceBegin(long tag, String name);
private static native void nativeTraceEnd(long tag);
+ private static native void nativeSetAppTracingAllowed(boolean allowed);
static {
// We configure two separate change callbacks, one in Trace.cpp and one here. The
@@ -111,6 +126,8 @@
*
* @param traceTag The trace tag to check.
* @return True if the trace tag is valid.
+ *
+ * @hide
*/
public static boolean isTagEnabled(long traceTag) {
long tags = sEnabledTags;
@@ -126,6 +143,8 @@
* @param traceTag The trace tag.
* @param counterName The counter name to appear in the trace.
* @param counterValue The counter value.
+ *
+ * @hide
*/
public static void traceCounter(long traceTag, String counterName, int counterValue) {
if (isTagEnabled(traceTag)) {
@@ -134,11 +153,28 @@
}
/**
- * Writes a trace message to indicate that a given method has begun.
- * Must be followed by a call to {@link #traceEnd} using the same tag.
+ * Set whether application tracing is allowed for this process. This is intended to be set
+ * once at application start-up time based on whether the application is debuggable.
+ *
+ * @hide
+ */
+ public static void setAppTracingAllowed(boolean allowed) {
+ nativeSetAppTracingAllowed(allowed);
+
+ // Setting whether app tracing is allowed may change the tags, so we update the cached
+ // tags here.
+ cacheEnabledTags();
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #traceEnd} using the same
+ * tag.
*
* @param traceTag The trace tag.
* @param methodName The method name to appear in the trace.
+ *
+ * @hide
*/
public static void traceBegin(long traceTag, String methodName) {
if (isTagEnabled(traceTag)) {
@@ -151,10 +187,48 @@
* Must be called exactly once for each call to {@link #traceBegin} using the same tag.
*
* @param traceTag The trace tag.
+ *
+ * @hide
*/
public static void traceEnd(long traceTag) {
if (isTagEnabled(traceTag)) {
nativeTraceEnd(traceTag);
}
}
+
+ /**
+ * Writes a trace message to indicate that a given section of code has begun. This call must
+ * be followed by a corresponding call to {@link #traceEnd()} on the same thread.
+ *
+ * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
+ * null character '\0' are used internally by the tracing mechanism. If sectionName contains
+ * these characters they will be replaced with a space character in the trace.
+ *
+ * @param sectionName The name of the code section to appear in the trace. This may be at
+ * most 127 Unicode code units long.
+ *
+ * @hide
+ */
+ public static void traceBegin(String sectionName) {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ if (sectionName.length() > MAX_SECTION_NAME_LEN) {
+ throw new IllegalArgumentException("sectionName is too long");
+ }
+ nativeTraceBegin(TRACE_TAG_APP, sectionName);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has ended. This call must
+ * be preceeded by a corresponding call to {@link #traceBegin(String)}. Calling this method
+ * will mark the end of the most recently begun section of code, so care must be taken to
+ * ensure that traceBegin / traceEnd pairs are properly nested and called from the same thread.
+ *
+ * @hide
+ */
+ public static void traceEnd() {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ nativeTraceEnd(TRACE_TAG_APP);
+ }
+ }
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 367d576..c41c35e 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -35,9 +35,7 @@
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Bundle;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -939,6 +937,15 @@
* its row id changed as a result of a sync or aggregation.
*/
public static final String LOOKUP_KEY = "lookup";
+
+ /**
+ * Timestamp (milliseconds since epoch) of when this contact was last updated. This
+ * includes updates to all data associated with this contact including raw contacts. Any
+ * modification (including deletes and inserts) of underlying contact data are also
+ * reflected in this timestamp.
+ */
+ public static final String CONTACT_LAST_UPDATED_TIMESTAMP =
+ "contact_last_updated_timestamp";
}
/**
@@ -2113,6 +2120,56 @@
return id >= Profile.MIN_ID;
}
+ protected interface DeletedContactsColumns {
+
+ /**
+ * A reference to the {@link ContactsContract.Contacts#_ID} that was deleted.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String CONTACT_ID = "contact_id";
+
+ /**
+ * Time (milliseconds since epoch) that the contact was deleted.
+ */
+ public static final String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ }
+
+ /**
+ * Constants for the deleted contact table. This table holds a log of deleted contacts.
+ * <p>
+ * Log older than {@link #DAYS_KEPT_MILLISECONDS} may be deleted.
+ */
+ public static final class DeletedContacts implements DeletedContactsColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private DeletedContacts() {
+ }
+
+ /**
+ * The content:// style URI for this table, which requests a directory of raw contact rows
+ * matching the selection criteria.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "deleted_contacts");
+
+ /**
+ * Number of days that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ *
+ * @hide
+ */
+ private static final int DAYS_KEPT = 30;
+
+ /**
+ * Milliseconds that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ */
+ public static final long DAYS_KEPT_MILLISECONDS = 1000L * 60L * 60L * 24L * (long)DAYS_KEPT;
+ }
+
+
protected interface RawContactsColumns {
/**
* A reference to the {@link ContactsContract.Contacts#_ID} that this
@@ -7909,6 +7966,13 @@
"android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
/**
+ * This is the intent that is fired when the contacts database is created. <p> The
+ * READ_CONTACT permission is required to receive these broadcasts.
+ */
+ public static final String CONTACTS_DATABASE_CREATED =
+ "android.provider.Contacts.DATABASE_CREATED";
+
+ /**
* Starts an Activity that lets the user pick a contact to attach an image to.
* After picking the contact it launches the image cropper in face detection mode.
*/
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8055077..8308459 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1368,7 +1368,7 @@
callbacks.onHardwarePreDraw(canvas);
if (displayList != null) {
- status = drawDisplayList(attachInfo, canvas, displayList, status);
+ status |= drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 26a5b26..85695fc 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -19,6 +19,7 @@
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
+import android.os.Trace;
import android.widget.FrameLayout;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -423,6 +424,8 @@
*/
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
+
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
@@ -520,6 +523,8 @@
mConstructorArgs[1] = null;
}
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+
return result;
}
}
@@ -547,6 +552,8 @@
Class<? extends View> clazz = null;
try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
+
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
@@ -615,6 +622,8 @@
+ (clazz == null ? "<unknown>" : clazz.getName()));
ie.initCause(e);
throw ie;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
deleted file mode 100644
index c889328..0000000
--- a/core/java/android/view/SimulatedDpad.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.util.Log;
-
-/**
- * This class creates DPAD events from TouchNavigation events.
- *
- * @see ViewRootImpl
- */
-
-//TODO: Make this class an internal class of ViewRootImpl.java
-class SimulatedDpad {
-
- private static final String TAG = "SimulatedDpad";
-
- // Maximum difference in milliseconds between the down and up of a touch
- // event for it to be considered a tap
- // TODO:Read this value from a configuration file
- private static final int MAX_TAP_TIME = 250;
- // Where the cutoff is for determining an edge swipe
- private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
- private static final int MSG_FLICK = 313;
- // TODO: Pass touch slop from the input device
- private static final int TOUCH_SLOP = 30;
- // The position of the previous TouchNavigation event
- private float mLastTouchNavigationXPosition;
- private float mLastTouchNavigationYPosition;
- // Where the Touch Navigation was initially pressed
- private float mTouchNavigationEnterXPosition;
- private float mTouchNavigationEnterYPosition;
- // When the most recent ACTION_HOVER_ENTER occurred
- private long mLastTouchNavigationStartTimeMs = 0;
- // When the most recent direction key was sent
- private long mLastTouchNavigationKeySendTimeMs = 0;
- // When the most recent touch event of any type occurred
- private long mLastTouchNavigationEventTimeMs = 0;
- // Did the swipe begin in a valid region
- private boolean mEdgeSwipePossible;
-
- private final Context mContext;
-
- // How quickly keys were sent;
- private int mKeySendRateMs = 0;
- private int mLastKeySent;
- // Last movement in device screen pixels
- private float mLastMoveX = 0;
- private float mLastMoveY = 0;
- // Offset from the initial touch. Gets reset as direction keys are sent.
- private float mAccumulatedX;
- private float mAccumulatedY;
-
- // Change in position allowed during tap events
- private float mTouchSlop;
- private float mTouchSlopSquared;
- // Has the TouchSlop constraint been invalidated
- private boolean mAlwaysInTapRegion = true;
-
- // Information from the most recent event.
- // Used to determine what device sent the event during a fling.
- private int mLastSource;
- private int mLastMetaState;
- private int mLastDeviceId;
-
- // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
- // read this from a config file instead
- private int mDistancePerTick;
- private int mDistancePerTickSquared;
- // Highest rate that the flinged events can occur at before dying out
- private int mMaxRepeatDelay;
- // The square of the minimum distance needed for a flick to register
- private int mMinFlickDistanceSquared;
- // How quickly the repeated events die off
- private float mFlickDecay;
-
- public SimulatedDpad(Context context) {
- mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64);
- mDistancePerTickSquared = mDistancePerTick * mDistancePerTick;
- mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300);
- mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20);
- mMinFlickDistanceSquared *= mMinFlickDistanceSquared;
- mFlickDecay = Float.parseFloat(SystemProperties.get(
- "persist.sys.vr_flick_decay", "1.3"));
- mTouchSlop = TOUCH_SLOP;
- mTouchSlopSquared = mTouchSlop * mTouchSlop;
-
- mContext = context;
- }
-
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_FLICK: {
- final long time = SystemClock.uptimeMillis();
- ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
- // Send the key
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
-
- // Increase the delay by the decay factor and resend
- final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
- if (delay <= mMaxRepeatDelay) {
- Message msgCopy = Message.obtain(msg);
- msgCopy.arg1 = delay;
- msgCopy.setAsynchronous(true);
- mHandler.sendMessageDelayed(msgCopy, delay);
- }
- break;
- }
- }
- }
- };
-
- public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
- boolean synthesizeNewKeys) {
- if (!synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- InputDevice device = event.getDevice();
- if (device == null) {
- return;
- }
- // Store what time the TouchNavigation event occurred
- final long time = SystemClock.uptimeMillis();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastTouchNavigationStartTimeMs = time;
- mAlwaysInTapRegion = true;
- mTouchNavigationEnterXPosition = event.getX();
- mTouchNavigationEnterYPosition = event.getY();
- mAccumulatedX = 0;
- mAccumulatedY = 0;
- mLastMoveX = 0;
- mLastMoveY = 0;
- if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * EDGE_SWIPE_THRESHOLD < event.getY()) {
- // Did the swipe begin in a valid region
- mEdgeSwipePossible = true;
- }
- // Clear any flings
- if (synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- // Determine whether the move is slop or an intentional move
- float deltaX = event.getX() - mTouchNavigationEnterXPosition;
- float deltaY = event.getY() - mTouchNavigationEnterYPosition;
- if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
- mAlwaysInTapRegion = false;
- }
- // Checks if the swipe has crossed the midpoint
- // and if our swipe gesture is complete
- if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * .5) && mEdgeSwipePossible) {
- mEdgeSwipePossible = false;
-
- Intent intent =
- ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF);
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e){
- Log.e(TAG, "Could not start search activity");
- }
- } else {
- Log.e(TAG, "Could not find a search activity");
- }
- }
- // Find the difference in position between the two most recent
- // TouchNavigation events
- mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
- mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
- mAccumulatedX += mLastMoveX;
- mAccumulatedY += mLastMoveY;
- float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
- float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY;
- // Determine if we've moved far enough to send a key press
- if (mAccumulatedXSquared > mDistancePerTickSquared ||
- mAccumulatedYSquared > mDistancePerTickSquared) {
- float dominantAxis;
- float sign;
- boolean isXAxis;
- int key;
- int repeatCount = 0;
- // Determine dominant axis
- if (mAccumulatedXSquared > mAccumulatedYSquared) {
- dominantAxis = mAccumulatedX;
- isXAxis = true;
- } else {
- dominantAxis = mAccumulatedY;
- isXAxis = false;
- }
- // Determine sign of axis
- sign = (dominantAxis > 0) ? 1 : -1;
- // Determine key to send
- if (isXAxis) {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT :
- KeyEvent.KEYCODE_DPAD_LEFT;
- } else {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- }
- // Send key until maximum distance constraint is satisfied
- while (dominantAxis * dominantAxis > mDistancePerTickSquared) {
- repeatCount++;
- dominantAxis -= sign * mDistancePerTick;
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- }
- }
- // Save new axis values
- mAccumulatedX = isXAxis ? dominantAxis : 0;
- mAccumulatedY = isXAxis ? 0 : dominantAxis;
-
- mLastKeySent = key;
- mKeySendRateMs = (int) (time - mLastTouchNavigationKeySendTimeMs) / repeatCount;
- mLastTouchNavigationKeySendTimeMs = time;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- }
- } else {
- float xMoveSquared = mLastMoveX * mLastMoveX;
- float yMoveSquared = mLastMoveY * mLastMoveY;
- // Determine whether the last gesture was a fling.
- if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
- time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
- mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
- mLastDeviceId = event.getDeviceId();
- mLastSource = event.getSource();
- mLastMetaState = event.getMetaState();
-
- if (synthesizeNewKeys) {
- Message message = Message.obtain(mHandler, MSG_FLICK,
- mKeySendRateMs, mLastKeySent, viewroot);
- message.setAsynchronous(true);
- mHandler.sendMessageDelayed(message, mKeySendRateMs);
- }
- }
- }
- mEdgeSwipePossible = false;
- break;
- }
-
- // Store touch event position and time
- mLastTouchNavigationEventTimeMs = time;
- mLastTouchNavigationXPosition = event.getX();
- mLastTouchNavigationYPosition = event.getY();
- }
-}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 22586f6..c1db4c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2923,8 +2923,6 @@
private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
- private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 25;
- private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 26;
final class ViewRootHandler extends Handler {
@Override
@@ -2974,10 +2972,6 @@
return "MSG_DISPATCH_DONE_ANIMATING";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_X_AXIS_KEY_REPEAT";
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_Y_AXIS_KEY_REPEAT";
}
return super.getMessageName(message);
}
@@ -3200,18 +3194,6 @@
invalidateWorld(mView);
}
} break;
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
- KeyEvent oldEvent = (KeyEvent)msg.obj;
- KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, SystemClock.uptimeMillis(),
- oldEvent.getRepeatCount() + 1);
- if (mAttachInfo.mHasWindowFocus) {
- enqueueInputEvent(e);
- Message m = obtainMessage(msg.what, e);
- m.setAsynchronous(true);
- sendMessageDelayed(m, mViewConfiguration.getKeyRepeatDelay());
- }
- } break;
}
}
}
@@ -3877,37 +3859,34 @@
}
/**
- * Performs default processing of unhandled input events.
+ * Performs synthesis of new input events from unhandled input events.
*/
final class SyntheticInputStage extends InputStage {
- private final TrackballAxis mTrackballAxisX = new TrackballAxis();
- private final TrackballAxis mTrackballAxisY = new TrackballAxis();
- private long mLastTrackballTime;
-
- private int mLastJoystickXDirection;
- private int mLastJoystickYDirection;
- private int mLastJoystickXKeyCode;
- private int mLastJoystickYKeyCode;
-
- private SimulatedDpad mSimulatedDpad;
+ private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
+ private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
+ private final SyntheticTouchNavigationHandler mTouchNavigation =
+ new SyntheticTouchNavigationHandler();
public SyntheticInputStage() {
super(null);
- mSimulatedDpad = new SimulatedDpad(mContext);
}
@Override
protected int onProcess(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
if (q.mEvent instanceof MotionEvent) {
- final int source = q.mEvent.getSource();
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return processTrackballEvent(q);
+ mTrackball.process(event);
+ return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- return processJoystickEvent(q);
+ mJoystick.process(event);
+ return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
- return processTouchNavigationEvent(q);
+ mTouchNavigation.process(event);
+ return FINISH_HANDLED;
}
}
return FORWARD;
@@ -3918,49 +3897,55 @@
if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
// Cancel related synthetic events if any prior stage has handled the event.
if (q.mEvent instanceof MotionEvent) {
- final int source = q.mEvent.getSource();
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- cancelTrackballEvent(q);
+ mTrackball.cancel(event);
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- cancelJoystickEvent(q);
+ mJoystick.cancel(event);
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
- cancelTouchNavigationEvent(q);
+ mTouchNavigation.cancel(event);
}
}
}
super.onDeliverToNext(q);
}
+ }
- private int processTrackballEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
+ /**
+ * Creates dpad events from unhandled trackball movements.
+ */
+ final class SyntheticTrackballHandler {
+ private final TrackballAxis mX = new TrackballAxis();
+ private final TrackballAxis mY = new TrackballAxis();
+ private long mLastTime;
+ public void process(MotionEvent event) {
// Translate the trackball event into DPAD keys and try to deliver those.
- final TrackballAxis x = mTrackballAxisX;
- final TrackballAxis y = mTrackballAxisY;
long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
+ if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) {
// It has been too long since the last movement,
// so restart at the beginning.
- x.reset(0);
- y.reset(0);
- mLastTrackballTime = curTime;
+ mX.reset(0);
+ mY.reset(0);
+ mLastTime = curTime;
}
final int action = event.getAction();
final int metaState = event.getMetaState();
switch (action) {
case MotionEvent.ACTION_DOWN:
- x.reset(2);
- y.reset(2);
+ mX.reset(2);
+ mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
break;
case MotionEvent.ACTION_UP:
- x.reset(2);
- y.reset(2);
+ mX.reset(2);
+ mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
@@ -3968,14 +3953,14 @@
break;
}
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
- + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+ if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
+ + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
+ " move=" + event.getX()
- + " / Y=" + y.position + " step="
- + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+ + " / Y=" + mY.position + " step="
+ + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration
+ " move=" + event.getY());
- final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
- final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+ final float xOff = mX.collect(event.getX(), event.getEventTime(), "X");
+ final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y");
// Generate DPAD events based on the trackball movement.
// We pick the axis that has moved the most as the direction of
@@ -3987,20 +3972,20 @@
int movement = 0;
float accel = 1;
if (xOff > yOff) {
- movement = x.generate();
+ movement = mX.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
: KeyEvent.KEYCODE_DPAD_LEFT;
- accel = x.acceleration;
- y.reset(2);
+ accel = mX.acceleration;
+ mY.reset(2);
}
} else if (yOff > 0) {
- movement = y.generate();
+ movement = mY.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
: KeyEvent.KEYCODE_DPAD_UP;
- accel = y.acceleration;
- x.reset(2);
+ accel = mY.acceleration;
+ mX.reset(2);
}
}
@@ -4034,16 +4019,12 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
}
- mLastTrackballTime = curTime;
+ mLastTime = curTime;
}
-
- // Unfortunately we can't tell whether the application consumed the keys, so
- // we always consider the trackball event handled.
- return FINISH_HANDLED;
}
- private void cancelTrackballEvent(QueuedInputEvent q) {
- mLastTrackballTime = Integer.MIN_VALUE;
+ public void cancel(MotionEvent event) {
+ mLastTime = Integer.MIN_VALUE;
// If we reach this, we consumed a trackball event.
// Because we will not translate the trackball event into a key event,
@@ -4052,19 +4033,220 @@
ensureTouchMode(false);
}
}
+ }
- private int processJoystickEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- updateJoystickDirection(event, true);
- return FINISH_HANDLED;
+ /**
+ * Maintains state information for a single trackball axis, generating
+ * discrete (DPAD) movements based on raw trackball motion.
+ */
+ static final class TrackballAxis {
+ /**
+ * The maximum amount of acceleration we will apply.
+ */
+ static final float MAX_ACCELERATION = 20;
+
+ /**
+ * The maximum amount of time (in milliseconds) between events in order
+ * for us to consider the user to be doing fast trackball movements,
+ * and thus apply an acceleration.
+ */
+ static final long FAST_MOVE_TIME = 150;
+
+ /**
+ * Scaling factor to the time (in milliseconds) between events to how
+ * much to multiple/divide the current acceleration. When movement
+ * is < FAST_MOVE_TIME this multiplies the acceleration; when >
+ * FAST_MOVE_TIME it divides it.
+ */
+ static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
+
+ static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
+ static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
+ static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
+
+ float position;
+ float acceleration = 1;
+ long lastMoveTime = 0;
+ int step;
+ int dir;
+ int nonAccelMovement;
+
+ void reset(int _step) {
+ position = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ step = _step;
+ dir = 0;
}
- private void cancelJoystickEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- updateJoystickDirection(event, false);
+ /**
+ * Add trackball movement into the state. If the direction of movement
+ * has been reversed, the state is reset before adding the
+ * movement (so that you don't have to compensate for any previously
+ * collected movement before see the result of the movement in the
+ * new direction).
+ *
+ * @return Returns the absolute value of the amount of movement
+ * collected so far.
+ */
+ float collect(float off, long time, String axis) {
+ long normTime;
+ if (off > 0) {
+ normTime = (long)(off * FAST_MOVE_TIME);
+ if (dir < 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = 1;
+ } else if (off < 0) {
+ normTime = (long)((-off) * FAST_MOVE_TIME);
+ if (dir > 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = -1;
+ } else {
+ normTime = 0;
+ }
+
+ // The number of milliseconds between each movement that is
+ // considered "normal" and will not result in any acceleration
+ // or deceleration, scaled by the offset we have here.
+ if (normTime > 0) {
+ long delta = time - lastMoveTime;
+ lastMoveTime = time;
+ float acc = acceleration;
+ if (delta < normTime) {
+ // The user is scrolling rapidly, so increase acceleration.
+ float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc *= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
+ } else {
+ // The user is scrolling slowly, so decrease acceleration.
+ float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc /= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc > 1 ? acc : 1;
+ }
+ }
+ position += off;
+ return Math.abs(position);
}
- private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+ /**
+ * Generate the number of discrete movement events appropriate for
+ * the currently collected trackball movement.
+ *
+ * @return Returns the number of discrete movements, either positive
+ * or negative, or 0 if there is not enough trackball movement yet
+ * for a discrete movement.
+ */
+ int generate() {
+ int movement = 0;
+ nonAccelMovement = 0;
+ do {
+ final int dir = position >= 0 ? 1 : -1;
+ switch (step) {
+ // If we are going to execute the first step, then we want
+ // to do this as soon as possible instead of waiting for
+ // a full movement, in order to make things look responsive.
+ case 0:
+ if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ step = 1;
+ break;
+ // If we have generated the first movement, then we need
+ // to wait for the second complete trackball motion before
+ // generating the second discrete movement.
+ case 1:
+ if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
+ step = 2;
+ break;
+ // After the first two, we generate discrete movements
+ // consistently with the trackball, applying an acceleration
+ // if the trackball is moving quickly. This is a simple
+ // acceleration on top of what we already compute based
+ // on how quickly the wheel is being turned, to apply
+ // a longer increasing acceleration to continuous movement
+ // in one direction.
+ default:
+ if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
+ float acc = acceleration;
+ acc *= 1.1f;
+ acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
+ break;
+ }
+ } while (true);
+ }
+ }
+
+ /**
+ * Creates dpad events from unhandled joystick movements.
+ */
+ final class SyntheticJoystickHandler extends Handler {
+ private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
+ private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
+
+ private int mLastXDirection;
+ private int mLastYDirection;
+ private int mLastXKeyCode;
+ private int mLastYKeyCode;
+
+ public SyntheticJoystickHandler() {
+ super(true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+ case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+ KeyEvent oldEvent = (KeyEvent)msg.obj;
+ KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
+ SystemClock.uptimeMillis(),
+ oldEvent.getRepeatCount() + 1);
+ if (mAttachInfo.mHasWindowFocus) {
+ enqueueInputEvent(e);
+ Message m = obtainMessage(msg.what, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay());
+ }
+ } break;
+ }
+ }
+
+ public void process(MotionEvent event) {
+ update(event, true);
+ }
+
+ public void cancel(MotionEvent event) {
+ update(event, false);
+ }
+
+ private void update(MotionEvent event, boolean synthesizeNewKeys) {
final long time = event.getEventTime();
final int metaState = event.getMetaState();
final int deviceId = event.getDeviceId();
@@ -4082,51 +4264,51 @@
yDirection = joystickAxisValueToDirection(event.getY());
}
- if (xDirection != mLastJoystickXDirection) {
- if (mLastJoystickXKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ if (xDirection != mLastXDirection) {
+ if (mLastXKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+ KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickXKeyCode = 0;
+ mLastXKeyCode = 0;
}
- mLastJoystickXDirection = xDirection;
+ mLastXDirection = xDirection;
if (xDirection != 0 && synthesizeNewKeys) {
- mLastJoystickXKeyCode = xDirection > 0
+ mLastXKeyCode = xDirection > 0
? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+ KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
}
- if (yDirection != mLastJoystickYDirection) {
- if (mLastJoystickYKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ if (yDirection != mLastYDirection) {
+ if (mLastYKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+ KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickYKeyCode = 0;
+ mLastYKeyCode = 0;
}
- mLastJoystickYDirection = yDirection;
+ mLastYDirection = yDirection;
if (yDirection != 0 && synthesizeNewKeys) {
- mLastJoystickYKeyCode = yDirection > 0
+ mLastYKeyCode = yDirection > 0
? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+ KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
}
}
@@ -4140,17 +4322,419 @@
return 0;
}
}
+ }
- private int processTouchNavigationEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, true);
- return FINISH_HANDLED;
+ /**
+ * Creates dpad events from unhandled touch navigation movements.
+ */
+ final class SyntheticTouchNavigationHandler extends Handler {
+ private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
+ private static final boolean LOCAL_DEBUG = false;
+
+ // Assumed nominal width and height in millimeters of a touch navigation pad,
+ // if no resolution information is available from the input system.
+ private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
+ private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
+
+ /* TODO: These constants should eventually be moved to ViewConfiguration. */
+
+ // Tap timeout in milliseconds.
+ private static final int TAP_TIMEOUT = 250;
+
+ // The maximum distance traveled for a gesture to be considered a tap in millimeters.
+ private static final int TAP_SLOP_MILLIMETERS = 5;
+
+ // The nominal distance traveled to move by one unit.
+ private static final int TICK_DISTANCE_MILLIMETERS = 12;
+
+ // Minimum and maximum fling velocity in ticks per second.
+ // The minimum velocity should be set such that we perform enough ticks per
+ // second that the fling appears to be fluid. For example, if we set the minimum
+ // to 2 ticks per second, then there may be up to half a second delay between the next
+ // to last and last ticks which is noticeably discrete and jerky. This value should
+ // probably not be set to anything less than about 4.
+ // If fling accuracy is a problem then consider tuning the tick distance instead.
+ private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
+ private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
+
+ // Fling velocity decay factor applied after each new key is emitted.
+ // This parameter controls the deceleration and overall duration of the fling.
+ // The fling stops automatically when its velocity drops below the minimum
+ // fling velocity defined above.
+ private static final float FLING_TICK_DECAY = 0.8f;
+
+ /* The input device that we are tracking. */
+
+ private int mCurrentDeviceId = -1;
+ private int mCurrentSource;
+ private boolean mCurrentDeviceSupported;
+
+ /* Configuration for the current input device. */
+
+ // The tap timeout and scaled slop.
+ private int mConfigTapTimeout;
+ private float mConfigTapSlop;
+
+ // The scaled tick distance. A movement of this amount should generally translate
+ // into a single dpad event in a given direction.
+ private float mConfigTickDistance;
+
+ // The minimum and maximum scaled fling velocity.
+ private float mConfigMinFlingVelocity;
+ private float mConfigMaxFlingVelocity;
+
+ /* Tracking state. */
+
+ // The velocity tracker for detecting flings.
+ private VelocityTracker mVelocityTracker;
+
+ // The active pointer id, or -1 if none.
+ private int mActivePointerId = -1;
+
+ // Time and location where tracking started.
+ private long mStartTime;
+ private float mStartX;
+ private float mStartY;
+
+ // Most recently observed position.
+ private float mLastX;
+ private float mLastY;
+
+ // Accumulated movement delta since the last direction key was sent.
+ private float mAccumulatedX;
+ private float mAccumulatedY;
+
+ // Set to true if any movement was delivered to the app.
+ // Implies that tap slop was exceeded.
+ private boolean mConsumedMovement;
+
+ // The most recently sent key down event.
+ // The keycode remains set until the direction changes or a fling ends
+ // so that repeated key events may be generated as required.
+ private long mPendingKeyDownTime;
+ private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+ private int mPendingKeyRepeatCount;
+ private int mPendingKeyMetaState;
+
+ // The current fling velocity while a fling is in progress.
+ private boolean mFlinging;
+ private float mFlingVelocity;
+
+ public SyntheticTouchNavigationHandler() {
+ super(true);
}
- private void cancelTouchNavigationEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, false);
+ public void process(MotionEvent event) {
+ // Update the current device information.
+ final long time = event.getEventTime();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
+ finishKeys(time);
+ finishTracking(time);
+ mCurrentDeviceId = deviceId;
+ mCurrentSource = source;
+ mCurrentDeviceSupported = false;
+ InputDevice device = event.getDevice();
+ if (device != null) {
+ // In order to support an input device, we must know certain
+ // characteristics about it, such as its size and resolution.
+ InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
+ InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
+ if (xRange != null && yRange != null) {
+ mCurrentDeviceSupported = true;
+
+ // Infer the resolution if it not actually known.
+ float xRes = xRange.getResolution();
+ if (xRes <= 0) {
+ xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
+ }
+ float yRes = yRange.getResolution();
+ if (yRes <= 0) {
+ yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
+ }
+ float nominalRes = (xRes + yRes) * 0.5f;
+
+ // Precompute all of the configuration thresholds we will need.
+ mConfigTapTimeout = TAP_TIMEOUT;
+ mConfigTapSlop = TAP_SLOP_MILLIMETERS * nominalRes;
+ mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
+ mConfigMinFlingVelocity =
+ MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+ mConfigMaxFlingVelocity =
+ MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
+ + " (" + Integer.toHexString(mCurrentSource) + "): "
+ + "mConfigTapTimeout=" + mConfigTapTimeout
+ + ", mConfigTapSlop=" + mConfigTapSlop
+ + ", mConfigTickDistance=" + mConfigTickDistance
+ + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
+ + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
+ }
+ }
+ }
+ }
+ if (!mCurrentDeviceSupported) {
+ return;
+ }
+
+ // Handle the event.
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ boolean caughtFling = mFlinging;
+ finishKeys(time);
+ finishTracking(time);
+ mActivePointerId = event.getPointerId(0);
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mStartTime = time;
+ mStartX = event.getX();
+ mStartY = event.getY();
+ mLastX = mStartX;
+ mLastY = mStartY;
+ mAccumulatedX = 0;
+ mAccumulatedY = 0;
+
+ // If we caught a fling, then pretend that the tap slop has already
+ // been exceeded to suppress taps whose only purpose is to stop the fling.
+ mConsumedMovement = caughtFling;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP: {
+ if (mActivePointerId < 0) {
+ break;
+ }
+ final int index = event.findPointerIndex(mActivePointerId);
+ if (index < 0) {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+
+ mVelocityTracker.addMovement(event);
+ final float x = event.getX(index);
+ final float y = event.getY(index);
+ mAccumulatedX += x - mLastX;
+ mAccumulatedY += y - mLastY;
+ mLastX = x;
+ mLastY = y;
+
+ // Consume any accumulated movement so far.
+ final int metaState = event.getMetaState();
+ consumeAccumulatedMovement(time, metaState);
+
+ // Detect taps and flings.
+ if (action == MotionEvent.ACTION_UP) {
+ if (!mConsumedMovement
+ && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
+ && time <= mStartTime + mConfigTapTimeout) {
+ // It's a tap!
+ finishKeys(time);
+ sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
+ sendKeyUp(time);
+ } else if (mConsumedMovement
+ && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ // It might be a fling.
+ mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
+ final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
+ final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
+ if (!startFling(time, vx, vy)) {
+ finishKeys(time);
+ }
+ }
+ finishTracking(time);
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+ }
}
+
+ public void cancel(MotionEvent event) {
+ if (mCurrentDeviceId == event.getDeviceId()
+ && mCurrentSource == event.getSource()) {
+ final long time = event.getEventTime();
+ finishKeys(time);
+ finishTracking(time);
+ }
+ }
+
+ private void finishKeys(long time) {
+ cancelFling();
+ sendKeyUp(time);
+ }
+
+ private void finishTracking(long time) {
+ if (mActivePointerId >= 0) {
+ mActivePointerId = -1;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ private void consumeAccumulatedMovement(long time, int metaState) {
+ final float absX = Math.abs(mAccumulatedX);
+ final float absY = Math.abs(mAccumulatedY);
+ if (absX >= absY) {
+ if (absX >= mConfigTickDistance) {
+ mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
+ KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
+ mAccumulatedY = 0;
+ mConsumedMovement = true;
+ }
+ } else {
+ if (absY >= mConfigTickDistance) {
+ mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
+ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
+ mAccumulatedX = 0;
+ mConsumedMovement = true;
+ }
+ }
+ }
+
+ private float consumeAccumulatedMovement(long time, int metaState,
+ float accumulator, int negativeKeyCode, int positiveKeyCode) {
+ while (accumulator <= -mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
+ accumulator += mConfigTickDistance;
+ }
+ while (accumulator >= mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
+ accumulator -= mConfigTickDistance;
+ }
+ return accumulator;
+ }
+
+ private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
+ if (mPendingKeyCode != keyCode) {
+ sendKeyUp(time);
+ mPendingKeyDownTime = time;
+ mPendingKeyCode = keyCode;
+ mPendingKeyRepeatCount = 0;
+ } else {
+ mPendingKeyRepeatCount += 1;
+ }
+ mPendingKeyMetaState = metaState;
+
+ // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1
+ // but it doesn't quite make sense when simulating the events in this way.
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
+ + ", repeatCount=" + mPendingKeyRepeatCount
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
+ mPendingKeyMetaState, mCurrentDeviceId,
+ KeyEvent.FLAG_FALLBACK, mCurrentSource));
+ }
+
+ private void sendKeyUp(long time) {
+ if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
+ mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
+ mCurrentSource));
+ mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+ }
+ }
+
+ private boolean startFling(long time, float vx, float vy) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
+ + ", min=" + mConfigMinFlingVelocity);
+ }
+
+ // Flings must be oriented in the same direction as the preceding movements.
+ switch (mPendingKeyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (-vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (-vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vy;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vy;
+ break;
+ }
+ return false;
+ }
+
+ // Post the first fling event.
+ mFlinging = postFling(time);
+ return mFlinging;
+ }
+
+ private boolean postFling(long time) {
+ // The idea here is to estimate the time when the pointer would have
+ // traveled one tick distance unit given the current fling velocity.
+ // This effect creates continuity of motion.
+ if (mFlingVelocity >= mConfigMinFlingVelocity) {
+ long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
+ postAtTime(mFlingRunnable, time + delay);
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Posted fling: velocity="
+ + mFlingVelocity + ", delay=" + delay
+ + ", keyCode=" + mPendingKeyCode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void cancelFling() {
+ if (mFlinging) {
+ removeCallbacks(mFlingRunnable);
+ mFlinging = false;
+ }
+ }
+
+ private final Runnable mFlingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final long time = SystemClock.uptimeMillis();
+ sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
+ mFlingVelocity *= FLING_TICK_DECAY;
+ if (!postFling(time)) {
+ mFlinging = false;
+ finishKeys(time);
+ }
+ }
+ };
}
/**
@@ -5487,174 +6071,6 @@
}
}
- /**
- * Maintains state information for a single trackball axis, generating
- * discrete (DPAD) movements based on raw trackball motion.
- */
- static final class TrackballAxis {
- /**
- * The maximum amount of acceleration we will apply.
- */
- static final float MAX_ACCELERATION = 20;
-
- /**
- * The maximum amount of time (in milliseconds) between events in order
- * for us to consider the user to be doing fast trackball movements,
- * and thus apply an acceleration.
- */
- static final long FAST_MOVE_TIME = 150;
-
- /**
- * Scaling factor to the time (in milliseconds) between events to how
- * much to multiple/divide the current acceleration. When movement
- * is < FAST_MOVE_TIME this multiplies the acceleration; when >
- * FAST_MOVE_TIME it divides it.
- */
- static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
-
- static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
- static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
- static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
-
- float position;
- float acceleration = 1;
- long lastMoveTime = 0;
- int step;
- int dir;
- int nonAccelMovement;
-
- void reset(int _step) {
- position = 0;
- acceleration = 1;
- lastMoveTime = 0;
- step = _step;
- dir = 0;
- }
-
- /**
- * Add trackball movement into the state. If the direction of movement
- * has been reversed, the state is reset before adding the
- * movement (so that you don't have to compensate for any previously
- * collected movement before see the result of the movement in the
- * new direction).
- *
- * @return Returns the absolute value of the amount of movement
- * collected so far.
- */
- float collect(float off, long time, String axis) {
- long normTime;
- if (off > 0) {
- normTime = (long)(off * FAST_MOVE_TIME);
- if (dir < 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = 1;
- } else if (off < 0) {
- normTime = (long)((-off) * FAST_MOVE_TIME);
- if (dir > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = -1;
- } else {
- normTime = 0;
- }
-
- // The number of milliseconds between each movement that is
- // considered "normal" and will not result in any acceleration
- // or deceleration, scaled by the offset we have here.
- if (normTime > 0) {
- long delta = time - lastMoveTime;
- lastMoveTime = time;
- float acc = acceleration;
- if (delta < normTime) {
- // The user is scrolling rapidly, so increase acceleration.
- float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc *= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
- } else {
- // The user is scrolling slowly, so decrease acceleration.
- float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc /= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc > 1 ? acc : 1;
- }
- }
- position += off;
- return Math.abs(position);
- }
-
- /**
- * Generate the number of discrete movement events appropriate for
- * the currently collected trackball movement.
- *
- * @return Returns the number of discrete movements, either positive
- * or negative, or 0 if there is not enough trackball movement yet
- * for a discrete movement.
- */
- int generate() {
- int movement = 0;
- nonAccelMovement = 0;
- do {
- final int dir = position >= 0 ? 1 : -1;
- switch (step) {
- // If we are going to execute the first step, then we want
- // to do this as soon as possible instead of waiting for
- // a full movement, in order to make things look responsive.
- case 0:
- if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- step = 1;
- break;
- // If we have generated the first movement, then we need
- // to wait for the second complete trackball motion before
- // generating the second discrete movement.
- case 1:
- if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
- step = 2;
- break;
- // After the first two, we generate discrete movements
- // consistently with the trackball, applying an acceleration
- // if the trackball is moving quickly. This is a simple
- // acceleration on top of what we already compute based
- // on how quickly the wheel is being turned, to apply
- // a longer increasing acceleration to continuous movement
- // in one direction.
- default:
- if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
- float acc = acceleration;
- acc *= 1.1f;
- acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
- break;
- }
- } while (true);
- }
- }
-
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
public CalledFromWrongThreadException(String msg) {
super(msg);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d901d0a..8ae0021 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1004,6 +1004,7 @@
* @param flag true if plugins should be enabled
* @deprecated This method has been deprecated in favor of
* {@link #setPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsEnabled(boolean flag) {
@@ -1032,6 +1033,7 @@
* @param pluginsPath a String path to the directory containing plugins
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsPath(String pluginsPath) {
@@ -1224,6 +1226,7 @@
* @return true if plugins are enabled
* @see #setPluginsEnabled
* @deprecated This method has been replaced by {@link #getPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized boolean getPluginsEnabled() {
@@ -1249,6 +1252,7 @@
* @return an empty string
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized String getPluginsPath() {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3fa0940..94dadb4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2606,7 +2606,7 @@
mGlobalLayoutListenerAddedFilter = false;
}
- if (mAdapter != null) {
+ if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
mDataSetObserver = null;
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index b6895a5..e3de0b9 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -494,20 +494,23 @@
// Make selected view and position it
mFirstPosition = mSelectedPosition;
- View sel = makeAndAddView(mSelectedPosition);
- int width = sel.getMeasuredWidth();
- int selectedOffset = childrenLeft;
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
- break;
- case Gravity.RIGHT:
- selectedOffset = childrenLeft + childrenWidth - width;
- break;
+
+ if (mAdapter != null) {
+ View sel = makeAndAddView(mSelectedPosition);
+ int width = sel.getMeasuredWidth();
+ int selectedOffset = childrenLeft;
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ selectedOffset = childrenLeft + childrenWidth - width;
+ break;
+ }
+ sel.offsetLeftAndRight(selectedOffset);
}
- sel.offsetLeftAndRight(selectedOffset);
// Flush any cached views that did not get reused above
mRecycler.clear();
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index b620568..04931e7 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -179,6 +179,9 @@
animateToTab(position);
}
}
+ if (mTabSpinner != null && position >= 0) {
+ mTabSpinner.setSelection(position);
+ }
}
public void setContentHeight(int contentHeight) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 66cea9d7..3e5586e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -179,6 +179,7 @@
libandroidfw \
libexpat \
libnativehelper \
+ liblog \
libcutils \
libutils \
libbinder \
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 9a9f6c8..11c7053 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -77,7 +77,14 @@
}
static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
- return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
+ if (bitmap) {
+ return new SkCanvas(*bitmap);
+ } else {
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
+ }
}
static void copyCanvasState(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index f028c86d..00ecd0a 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -18,6 +18,9 @@
#include <JNIHelp.h>
#include <ScopedUtfChars.h>
+#include <ScopedStringChars.h>
+
+#include <utils/String8.h>
#include <cutils/trace.h>
#include <cutils/log.h>
@@ -36,8 +39,20 @@
static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz,
jlong tag, jstring nameStr) {
- ScopedUtfChars name(env, nameStr);
- atrace_begin(tag, name.c_str());
+ const size_t MAX_SECTION_NAME_LEN = 127;
+ ScopedStringChars jchars(env, nameStr);
+ String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()),
+ jchars.size());
+ size_t size = utf8Chars.size();
+ char* str = utf8Chars.lockBuffer(size);
+ for (size_t i = 0; i < size; i++) {
+ char c = str[i];
+ if (c == '\0' || c == '\n' || c == '|') {
+ str[i] = ' ';
+ }
+ }
+ utf8Chars.unlockBuffer();
+ atrace_begin(tag, utf8Chars.string());
}
static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
@@ -45,6 +60,11 @@
atrace_end(tag);
}
+static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env,
+ jclass clazz, jboolean allowed) {
+ atrace_set_debuggable(allowed);
+}
+
static JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{ "nativeGetEnabledTags",
@@ -59,6 +79,9 @@
{ "nativeTraceEnd",
"(J)V",
(void*)android_os_Trace_nativeTraceEnd },
+ { "nativeSetAppTracingAllowed",
+ "(Z)V",
+ (void*)android_os_Trace_nativeSetAppTracingAllowed },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 90e3b8d..8ebc88c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -616,6 +616,14 @@
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
+ <!-- Allows an application to use location features in hardware,
+ such as the geofencing api
+ Protected by signature|system protection level -->
+ <permission android:name="android.permission.LOCATION_HARDWARE"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="signature|system" />
+ <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
@@ -2350,6 +2358,9 @@
android:permission="android.permission.MASTER_CLEAR"
android:exported="true" />
+ <service android:name="android.hardware.location.GeofenceHardwareService"
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
</application>
</manifest>
diff --git a/core/res/res/anim/rotation_animation_xfade_exit.xml b/core/res/res/anim/rotation_animation_xfade_exit.xml
index 7300724..1dedde4 100644
--- a/core/res/res/anim/rotation_animation_xfade_exit.xml
+++ b/core/res/res/anim/rotation_animation_xfade_exit.xml
@@ -18,6 +18,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="500"
+ android:duration="150"
android:interpolator="@interpolator/decelerate_quad" />
</set>
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e5ff886
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..06d1653
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..2020a42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..7cae402
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e85b0c2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..eea215d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_panel_holo_dark.xml b/core/res/res/drawable/menu_panel_holo_dark.xml
new file mode 100644
index 0000000..658a3ac
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_dark" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/menu_panel_holo_light.xml b/core/res/res/drawable/menu_panel_holo_light.xml
new file mode 100644
index 0000000..a37e934
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_light" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_light" />
+</selector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 65363da..ce76d40 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksaksies"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8ec00c3..132d4cc 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"የፅሁፍ እርምጃዎች"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> በማሄድ ላይ"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> በአሁኑ ጊዜ እያሄደ ነው"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"እሺ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="yes" msgid="5362982303337969312">"እሺ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 688f524..e13c0f2 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"إجراءات النص"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل حاليًا"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"موافق"</string>
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="yes" msgid="5362982303337969312">"موافق"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 4c88a71..6653ac1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> працуе"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> зараз працуе"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"ОК"</string>
<string name="cancel" msgid="6442560571259935130">"Адмяніць"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 9ff9321..3d3e195 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Действия с текста"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Понастоящем <xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 6dff5d1..c96bef8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"S\'està acabant l\'espai d\'emmagatzematge"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant en aquests moments"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"D\'acord"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="yes" msgid="5362982303337969312">"D\'acord"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7036fbe..4738834 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Operace s textem"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je spuštěna"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je aktuálně spuštěná"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 40dcfbd..6c16e56 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører i øjeblikket"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 51a8504..ead4004 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt."</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird gerade ausgeführt."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index a30b8cf..ac4ec2e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ενέργειες κειμένου"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο χώρος αποθήκευσης εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται τώρα."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 060322d..5285532 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> running"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is currently running"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f0786a9..20f0c33 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en ejecución"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en este momento."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Aceptar"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index ec3308d..7dc03bec 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Aceptar"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8ec5525..e78e169 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab praegu"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Tühista"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d64265b..3ab64c0 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"عملکردهای متنی"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"فضای ذخیرهسازی رو به اتمام است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال حاضر در حال اجرا است"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"تأیید"</string>
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="yes" msgid="5362982303337969312">"تأیید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 131d937..86a9fae 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoiminnot"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9fe514d..3b539da8 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en cours d\'exécution"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> est en cours d\'exécution."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 2fc5e2a..ac20919 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"पाठ क्रियाएं"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संग्रहण स्थान समाप्त हो रहा है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> चल रहा है"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान में चल रहा है"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"ठीक"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
<string name="yes" msgid="5362982303337969312">"ठीक"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9c45b4b..9569a6c 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Izvodi se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutačno se izvodi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"U redu"</string>
<string name="cancel" msgid="6442560571259935130">"Odustani"</string>
<string name="yes" msgid="5362982303337969312">"U redu"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 96380cd..61e3707 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> fut"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg fut"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 40c466b..f4664e9 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Oke"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"Oke"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 79c1f51..592492a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> in esecuzione"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> è attualmente in esecuzione"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5430506..97c52c5 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל כרגע"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"אישור"</string>
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="yes" msgid="5362982303337969312">"אישור"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index a828c41..0abacb1 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>を実行中"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"現在<xliff:g id="APP_NAME">%1$s</xliff:g>を実行しています"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 9450a49..b07878c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"텍스트 작업"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> 실행 중"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>이(가) 현재 실행 중입니다."</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"확인"</string>
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="yes" msgid="5362982303337969312">"확인"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e20e1c9..1b7b68e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksto veiksmai"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ paleista"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu paleista"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Gerai"</string>
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="yes" msgid="5362982303337969312">"Gerai"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3bf284f..9ae5381d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> darbojas"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik darbojas"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Labi"</string>
<string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
<string name="yes" msgid="5362982303337969312">"Labi"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 894352d..bbde90d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c0f316f..3bb5022 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører for øyeblikket"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 77e243e..4d71f08 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> is actief"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel actief"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index cae4008..f4fe466 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> uruchomiona"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest uruchomiona"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 407ad6d..36751b1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está a ser executado"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a14ebdd..61716fe 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está em execução"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1425,7 +1427,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
<string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
- <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes do dock"</string>
+ <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
<string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index b850bfd..ba863cb 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1638,9 +1638,9 @@
<skip />
<!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
<skip />
- <!-- no translation found for app_running_notification_title (4625479411505090209) -->
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
<skip />
- <!-- no translation found for app_running_notification_text (3368349329989620597) -->
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
<skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Interrumper"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f48e540..003f932 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcţii de sistem să nu funcţioneze"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Anulaţi"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index eed5868..2c8dfe8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Заканчивается свободное место"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые системные функции могут не работать"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется в данный момент"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"ОК"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index a08262e..80a2c31 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je práve spustená"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0d09190..766b605 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Izvaja se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutno se izvaja aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"V redu"</string>
<string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
<string name="yes" msgid="5362982303337969312">"V redu"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 5eff70b..15b9ec5 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Радње у вези са текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Простор за складиштење је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> је тренутно покренута"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Потврди"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="yes" msgid="5362982303337969312">"Потврди"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4b831af..291681c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index b770143..c8f1343 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Vitendo vya maandishi"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhafadhi inakwisha"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa kwa sasa"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Sawa"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="yes" msgid="5362982303337969312">"Sawa"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index a8bb17b..66b3199 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"การทำงานของข้อความ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงาน"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงานในขณะนี้"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"ตกลง"</string>
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="yes" msgid="5362982303337969312">"ตกลง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 323a673..bd5f99d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Kasalukuyang tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 414e9bd9..79c3706 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Metin eylemleri"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> çalışıyor"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> şu anda çalışıyor"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"Tamam"</string>
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="yes" msgid="5362982303337969312">"Tamam"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cb0b863..ad206d3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> працює"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> зараз працює"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1880369..4022f04 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện đang chạy"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7b52f15..5ebd3c7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在运行"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前正在运行"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"确定"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"确定"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 3faf789..a0c8c05 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在執行"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前正在執行"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"確定"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"確定"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index afd183d..7f62603 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1052,8 +1052,10 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Izenzo zombhalo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> isebenza manje"</string>
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
+ <skip />
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
+ <skip />
<string name="ok" msgid="5970060430562524910">"KULUNGILE"</string>
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
<string name="yes" msgid="5362982303337969312">"KULUNGILE"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f7ff77b..146607e 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -141,6 +141,10 @@
<item>@drawable/menu_submenu_background</item>
<item>@drawable/menu_dropdown_panel_holo_light</item>
<item>@drawable/menu_dropdown_panel_holo_dark</item>
+ <item>@drawable/menu_popup_panel_holo_light</item>
+ <item>@drawable/menu_popup_panel_holo_dark</item>
+ <item>@drawable/menu_panel_holo_light</item>
+ <item>@drawable/menu_panel_holo_dark</item>
<item>@drawable/overscroll_edge</item>
<item>@drawable/overscroll_glow</item>
<item>@drawable/spinner_16_outer_holo</item>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 56c2235..f494d8c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1869,7 +1869,7 @@
<style name="Widget.Holo.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_dark</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_dark</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
@@ -2242,7 +2242,7 @@
<style name="Widget.Holo.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_light</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_light</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
index 9c44d61..0518e64 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
@@ -265,8 +265,8 @@
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleSwitching() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
doCommonDownloadSetup();
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
@@ -340,8 +340,8 @@
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleWiFiEnableDisable() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
doCommonDownloadSetup();
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
@@ -409,8 +409,8 @@
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleAirplaneModeEnableDisable() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
// make sure WiFi is enabled, and airplane mode is not on
doCommonDownloadSetup();
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index bd57775..4642d6f 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index 5630e63..3ec71b2 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -138,7 +138,7 @@
<li>{@link android.opengl.GLES10}</li>
<li>{@link android.opengl.GLES10Ext}</li>
<li>{@link android.opengl.GLES11}</li>
- <li>{@link android.opengl.GLES10Ext}</li>
+ <li>{@link android.opengl.GLES11Ext}</li>
</ul>
</li>
<li>{@link javax.microedition.khronos.opengles} - This package provides the standard
@@ -289,13 +289,14 @@
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
- // the coordinates of objects that use this vertex shader
+ // the coordinates of objects that use this vertex shader.
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
-
- // the matrix must be included as part of gl_Position
+ // The matrix must be included as part of gl_Position
+ // Note that the uMVPMatrix factor *must be first* in order
+ // for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
diff --git a/docs/html/tools/debugging/debugging-ui.jd b/docs/html/tools/debugging/debugging-ui.jd
index 8ca5192..f927d08 100644
--- a/docs/html/tools/debugging/debugging-ui.jd
+++ b/docs/html/tools/debugging/debugging-ui.jd
@@ -60,7 +60,9 @@
<p>The Hierarchy Viewer application allows you to debug and optimize your user interface. It
provides a visual representation of the layout's View hierarchy (the View Hierarchy window)
- and a magnified view of the display (the Pixel Perfect window).</p>
+ with performance information for each node in the layout,
+ and a magnified view of the display (the Pixel Perfect window) to closely examine the pixels
+ in your layout.</p>
<p>Android <code>lint</code> is a static code scanning tool that helps you optimize the layouts and layout
hierarchies of your applications, as well as detect other common coding problems. You can run it against your layout files or resource
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index fff7eee..474b9b2 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SHARED_LIBRARIES := \
libdrmframework \
+ liblog \
libutils \
libandroid_runtime \
libnativehelper \
@@ -43,4 +44,3 @@
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index b8564b6..5751331 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -274,12 +274,20 @@
}
}
+ // don't need to account for USAGE_SHARED Allocations
+ if ((usage & USAGE_SHARED) == 0) {
+ int numBytes = t.getCount() * t.getElement().getBytesSize();
+ rs.addAllocSizeForGC(numBytes);
+ mGCSize = numBytes;
+ }
+
mType = t;
mUsage = usage;
if (t != null) {
updateCacheInfo(t);
}
+
}
private void validateIsInt32() {
@@ -492,7 +500,9 @@
*/
public void copyFromUnchecked(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -507,7 +517,9 @@
*/
public void copyFromUnchecked(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -522,7 +534,9 @@
*/
public void copyFromUnchecked(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -537,7 +551,9 @@
*/
public void copyFromUnchecked(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -553,7 +569,9 @@
*/
public void copyFrom(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -569,7 +587,9 @@
*/
public void copyFrom(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -585,7 +605,9 @@
*/
public void copyFrom(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -601,7 +623,9 @@
*/
public void copyFrom(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -967,12 +991,144 @@
Canvas c = new Canvas(newBitmap);
c.drawBitmap(data, 0, 0, null);
copy2DRangeFrom(xoff, yoff, newBitmap);
+ return;
}
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
}
+ private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
+ if (mAdaptedAllocation != null) {
+
+ } else {
+
+ if (xoff < 0 || yoff < 0 || zoff < 0) {
+ throw new RSIllegalArgumentException("Offset cannot be negative.");
+ }
+ if (h < 0 || w < 0 || d < 0) {
+ throw new RSIllegalArgumentException("Height or width cannot be negative.");
+ }
+ if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY) || ((zoff + d) > mCurrentDimZ)) {
+ throw new RSIllegalArgumentException("Updated region larger than allocation.");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 2);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+
+ /**
+ * @hide
+ * Copy a rectangular region from the array into the allocation.
+ * The incoming array is assumed to be tightly packed.
+ *
+ * @param xoff X offset of the region to update
+ * @param yoff Y offset of the region to update
+ * @param zoff Z offset of the region to update
+ * @param w Width of the incoming region to update
+ * @param h Height of the incoming region to update
+ * @param d Depth of the incoming region to update
+ * @param data to be placed into the allocation
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ validateIsInt8();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ validateIsInt16();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ validateIsInt32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ validateIsFloat32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ * Copy a rectangular region into the allocation from another
+ * allocation.
+ *
+ * @param xoff X offset of the region to update.
+ * @param yoff Y offset of the region to update.
+ * @param w Width of the incoming region to update.
+ * @param h Height of the incoming region to update.
+ * @param d Depth of the incoming region to update.
+ * @param data source allocation.
+ * @param dataXoff X offset in data of the region to update.
+ * @param dataYoff Y offset in data of the region to update.
+ * @param dataZoff Z offset in data of the region to update
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d,
+ Allocation data, int dataXoff, int dataYoff, int dataZoff) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data.getID(mRS), dataXoff, dataYoff, dataZoff,
+ data.mSelectedLOD);
+ }
+
/**
* Copy from the Allocation into a Bitmap. The bitmap must
@@ -1050,6 +1206,10 @@
* A new type will be created with the new dimension.
*
* @param dimX The new size of the allocation.
+ *
+ * @deprecated Renderscript objects should be immutable once
+ * created. The replacement is to create a new allocation and copy the
+ * contents.
*/
public synchronized void resize(int dimX) {
if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
@@ -1064,38 +1224,6 @@
updateCacheInfo(mType);
}
- /**
- * Resize a 2D allocation. The contents of the allocation are
- * preserved. If new elements are allocated objects are created
- * with null contents and the new region is otherwise undefined.
- *
- * If the new region is smaller the references of any objects
- * outside the new region will be released.
- *
- * A new type will be created with the new dimension.
- *
- * @param dimX The new size of the allocation.
- * @param dimY The new size of the allocation.
- */
- public synchronized void resize(int dimX, int dimY) {
- if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- if (mType.getY() == 0) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- mRS.nAllocationResize2D(getID(mRS), dimX, dimY);
- mRS.finish(); // Necessary because resize is fifoed and update is async.
-
- int typeID = mRS.nAllocationGetType(getID(mRS));
- mType = new Type(typeID, mRS);
- mType.updateFromNative();
- updateCacheInfo(mType);
- }
-
-
// creation
@@ -1117,6 +1245,7 @@
if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
}
+
int id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
@@ -1266,7 +1395,6 @@
return alloc;
}
-
int id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
if (id == 0) {
throw new RSRuntimeException("Load failed.");
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index f464f9b..c2ebc9f 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -71,6 +71,9 @@
private int mID;
private boolean mDestroyed;
private String mName;
+
+ int mGCSize;
+
RenderScript mRS;
/**
@@ -135,6 +138,9 @@
throw new RSInvalidStateException("Object already destroyed.");
}
mDestroyed = true;
+ if (mGCSize != 0) {
+ mRS.removeAllocSizeForGC(mGCSize);
+ }
mRS.nObjDestroy(mID);
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index bef28aa..d5af276 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -83,11 +83,7 @@
native void nContextInitToClient(int con);
native void nContextDeinitToClient(int con);
- /**
- * Name of the file that holds the object cache.
- */
- private static final String CACHE_PATH = "com.android.renderscript.cache";
- static String mCachePath;
+ static File mCacheDir;
/**
* Sets the directory to use as a persistent storage for the
@@ -97,9 +93,8 @@
* @param cacheDir A directory the current process can write to
*/
public static void setupDiskCache(File cacheDir) {
- File f = new File(cacheDir, CACHE_PATH);
- mCachePath = f.getAbsolutePath();
- f.mkdirs();
+ // Defer creation of cache path to nScriptCCreate().
+ mCacheDir = cacheDir;
}
public enum ContextType {
@@ -420,6 +415,46 @@
rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, b);
}
+ native void rsnAllocationData3D(int con,
+ int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip);
+ synchronized void nAllocationData3D(int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip) {
+ validate();
+ rsnAllocationData3D(mContext,
+ dstAlloc, dstXoff, dstYoff, dstZoff,
+ dstMip, width, height, depth,
+ srcAlloc, srcXoff, srcYoff, srcZoff, srcMip);
+ }
+
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+
+
native void rsnAllocationRead(int con, int id, byte[] d);
synchronized void nAllocationRead(int id, byte[] d) {
validate();
@@ -451,11 +486,6 @@
validate();
rsnAllocationResize1D(mContext, id, dimX);
}
- native void rsnAllocationResize2D(int con, int id, int dimX, int dimY);
- synchronized void nAllocationResize2D(int id, int dimX, int dimY) {
- validate();
- rsnAllocationResize2D(mContext, id, dimX, dimY);
- }
native int rsnFileA3DCreateFromAssetStream(int con, int assetStream);
synchronized int nFileA3DCreateFromAssetStream(int assetStream) {
@@ -724,6 +754,8 @@
int mContext;
@SuppressWarnings({"FieldCanBeLocal"})
MessageThread mMessageThread;
+ GCThread mGCThread;
+
Element mElement_U8;
Element mElement_I8;
@@ -1006,6 +1038,49 @@
}
}
+ static class GCThread extends Thread {
+ RenderScript mRS;
+ boolean mRun = true;
+
+ int currentSize = 0;
+ final static int targetSize = 256*1024*1024; // call System.gc after 256MB of allocs
+
+ GCThread(RenderScript rs) {
+ super("RSGCThread");
+ mRS = rs;
+
+ }
+
+ public void run() {
+ while(mRun) {
+ boolean doGC = false;
+ synchronized(this) {
+ if (currentSize >= targetSize) {
+ doGC = true;
+ }
+ }
+ if (doGC == true) {
+ System.gc();
+ }
+ try {
+ sleep(1, 0);
+ } catch(InterruptedException e) {
+ }
+ }
+ Log.d(LOG_TAG, "GCThread exiting.");
+ }
+
+ public synchronized void addAllocSize(int bytes) {
+ currentSize += bytes;
+ }
+
+ public synchronized void removeAllocSize(int bytes) {
+ currentSize -= bytes;
+ }
+
+ }
+
+
RenderScript(Context ctx) {
if (ctx != null) {
mApplicationContext = ctx.getApplicationContext();
@@ -1028,6 +1103,15 @@
return create(ctx, sdkVersion, ContextType.NORMAL);
}
+ void addAllocSizeForGC(int bytes) {
+ mGCThread.addAllocSize(bytes);
+ }
+
+ void removeAllocSizeForGC(int bytes) {
+ mGCThread.removeAllocSize(bytes);
+ }
+
+
/**
* Create a basic RenderScript context.
*
@@ -1044,7 +1128,9 @@
throw new RSDriverException("Failed to create RS context.");
}
rs.mMessageThread = new MessageThread(rs);
+ rs.mGCThread = new GCThread(rs);
rs.mMessageThread.start();
+ rs.mGCThread.start();
return rs;
}
@@ -1099,8 +1185,10 @@
validate();
nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
+ mGCThread.mRun = false;
try {
mMessageThread.join();
+ mGCThread.join();
} catch(InterruptedException e) {
}
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 52034b1..fad8838 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -198,6 +198,9 @@
}
mMessageThread = new MessageThread(this);
mMessageThread.start();
+ mGCThread = new GCThread(this);
+ mGCThread.start();
+
}
/**
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index 108b230..2f69775 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -60,8 +60,16 @@
throw new RSRuntimeException("Loading of ScriptC script failed.");
}
setID(id);
+ mGCSize = 2 * 1024 * 1024;
+ rs.addAllocSizeForGC(mGCSize);
}
+ /**
+ * Name of the file that holds the object cache.
+ */
+ private static final String CACHE_PATH = "com.android.renderscript.cache";
+
+ static String mCachePath;
private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID) {
byte[] pgm;
@@ -94,7 +102,13 @@
String resName = resources.getResourceEntryName(resourceID);
+ // Create the RS cache path if we haven't done so already.
+ if (mCachePath == null) {
+ File f = new File(rs.mCacheDir, CACHE_PATH);
+ mCachePath = f.getAbsolutePath();
+ f.mkdirs();
+ }
Log.v(TAG, "Create script for resource = " + resName);
- return rs.nScriptCCreate(resName, rs.mCachePath, pgm, pgmLength);
+ return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
}
}
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 80d7728..e8beae53 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -10,6 +10,7 @@
libnativehelper \
libRS \
libcutils \
+ liblog \
libskia \
libutils \
libui \
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 8757b19..460a516 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -725,6 +725,72 @@
}
static void
+nAllocationData3D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jshortArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_s, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jshort *ptr = _env->GetShortArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jbyteArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_b, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jintArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jfloatArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_alloc(JNIEnv *_env, jobject _this, RsContext con,
+ jint dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
+ jint dstMip,
+ jint width, jint height, jint depth,
+ jint srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
+ jint srcMip)
+{
+ LOG_API("nAllocationData3D_alloc, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
+ " dstMip(%i), width(%i), height(%i),"
+ " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i)",
+ con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
+ width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
+
+ rsAllocationCopy3DRange(con,
+ (RsAllocation)dstAlloc,
+ dstXoff, dstYoff, dstZoff, dstMip,
+ width, height, depth,
+ (RsAllocation)srcAlloc,
+ srcXoff, srcYoff, srcZoff, srcMip);
+}
+
+static void
nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
{
jint len = _env->GetArrayLength(data);
@@ -782,13 +848,6 @@
rsAllocationResize1D(con, (RsAllocation)alloc, dimX);
}
-static void
-nAllocationResize2D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX, jint dimY)
-{
- LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i), sizeY(%i)", con, (RsAllocation)alloc, dimX, dimY);
- rsAllocationResize2D(con, (RsAllocation)alloc, dimX, dimY);
-}
-
// -----------------------------------
static int
@@ -1519,13 +1578,17 @@
{"rsnAllocationData2D", "(IIIIIIII[BI)V", (void*)nAllocationData2D_b },
{"rsnAllocationData2D", "(IIIIIIII[FI)V", (void*)nAllocationData2D_f },
{"rsnAllocationData2D", "(IIIIIIIIIIIII)V", (void*)nAllocationData2D_alloc },
+{"rsnAllocationData3D", "(IIIIIIIII[II)V", (void*)nAllocationData3D_i },
+{"rsnAllocationData3D", "(IIIIIIIII[SI)V", (void*)nAllocationData3D_s },
+{"rsnAllocationData3D", "(IIIIIIIII[BI)V", (void*)nAllocationData3D_b },
+{"rsnAllocationData3D", "(IIIIIIIII[FI)V", (void*)nAllocationData3D_f },
+{"rsnAllocationData3D", "(IIIIIIIIIIIIII)V", (void*)nAllocationData3D_alloc },
{"rsnAllocationRead", "(II[I)V", (void*)nAllocationRead_i },
{"rsnAllocationRead", "(II[S)V", (void*)nAllocationRead_s },
{"rsnAllocationRead", "(II[B)V", (void*)nAllocationRead_b },
{"rsnAllocationRead", "(II[F)V", (void*)nAllocationRead_f },
{"rsnAllocationGetType", "(II)I", (void*)nAllocationGetType},
{"rsnAllocationResize1D", "(III)V", (void*)nAllocationResize1D },
-{"rsnAllocationResize2D", "(IIII)V", (void*)nAllocationResize2D },
{"rsnAllocationGenerateMipmaps", "(II)V", (void*)nAllocationGenerateMipmaps },
{"rsnScriptBindAllocation", "(IIII)V", (void*)nScriptBindAllocation },
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 06e658d..7b59bf2 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -58,7 +58,7 @@
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui libRS libRScpp
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 9bc5c14..51f1e39 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -38,6 +38,10 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (useFloatTexture) {
+ // We use a R16F texture, let's remap the alpha channel to the
+ // red channel to avoid changing the shader sampling code on GL ES 3.0+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
+
float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
const GLfloat pattern[] = {
0 * dither, 8 * dither, 2 * dither, 10 * dither,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3730017..e18d922 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1905,14 +1905,15 @@
status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
int32_t replayFlags) {
+ status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- startFrame();
+ status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
displayList->replay(replayStruct, 0);
- return replayStruct.mDrawGlStatus;
+ return status | replayStruct.mDrawGlStatus;
}
DeferredDisplayList deferredList;
@@ -1920,9 +1921,9 @@
displayList->defer(deferStruct, 0);
flushLayers();
- startFrame();
+ status = startFrame();
- return deferredList.flush(*this, dirty);
+ return status | deferredList.flush(*this, dirty);
}
return DrawGlInfo::kStatusDone;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 2479630..8eb85e5 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -186,7 +186,7 @@
// ES 2.0
"texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
// ES 3.0
- "texture2D(ditherSampler, ditherTexCoords).r"
+ "texture2D(ditherSampler, ditherTexCoords).a"
};
const char* gFS_Main_AddDitherToGradient =
" gradientColor += %s;\n";
diff --git a/location/java/android/location/IGeofenceProvider.aidl b/location/java/android/location/IGeofenceProvider.aidl
new file mode 100644
index 0000000..5a5fdc6
--- /dev/null
+++ b/location/java/android/location/IGeofenceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.location;
+
+import android.hardware.location.IGeofenceHardware;
+
+/**
+ * An interface for location providers implementing the Geofencing service
+ *
+ * {@hide}
+ */
+interface IGeofenceProvider {
+ void setGeofenceHardware(in IGeofenceHardware proxy);
+}
diff --git a/location/java/android/location/IGpsGeofenceHardware.aidl b/location/java/android/location/IGpsGeofenceHardware.aidl
new file mode 100644
index 0000000..764bf8e
--- /dev/null
+++ b/location/java/android/location/IGpsGeofenceHardware.aidl
@@ -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.
+ */
+
+package android.location;
+
+/**
+ * GPS hardware geofence
+ *
+ * @hide
+ */
+interface IGpsGeofenceHardware
+{
+ boolean isHardwareGeofenceSupported();
+ boolean addCircularHardwareGeofence(int geofenceId, double latitude, double
+ longitude, double radius, int lastTransition, int monitorTransition,
+ int notificationResponsiveness, int unknownTimer);
+ boolean removeHardwareGeofence(int geofenceId);
+ boolean pauseHardwareGeofence(int geofenceId);
+ boolean resumeHardwareGeofence(int geofenceId, int monitorTransition);
+}
diff --git a/location/lib/java/com/android/location/provider/GeofenceProvider.java b/location/lib/java/com/android/location/provider/GeofenceProvider.java
new file mode 100644
index 0000000..2618f34
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/GeofenceProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.location.provider;
+
+import android.hardware.location.GeofenceHardware;
+import android.hardware.location.IGeofenceHardware;
+import android.os.IBinder;
+
+import android.location.IGeofenceProvider;
+import android.util.Log;
+
+import java.lang.Long;
+
+/**
+ * Base class for geofence providers implemented as unbundled services.
+ *
+ * <p>Geofence providers can be implemented as services and return the result of
+ * {@link com.android.location.provider.GeofenceProvider#getBinder()} in its getBinder() method.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
+ */
+public abstract class GeofenceProvider {
+
+ private GeofenceHardware mGeofenceHardware;
+
+ private IGeofenceProvider.Stub mProvider = new IGeofenceProvider.Stub() {
+ public void setGeofenceHardware(IGeofenceHardware hardwareProxy) {
+ mGeofenceHardware = new GeofenceHardware(hardwareProxy);
+ onGeofenceHardwareChange(mGeofenceHardware);
+ }
+ };
+
+ /**
+ * Returns the Binder interface for the geofence provider.
+ * This is intended to be used for the onBind() method of
+ * a service that implements a geofence service.
+ *
+ * @return the IBinder instance for the provider
+ */
+ public IBinder getBinder() {
+ return mProvider;
+ }
+
+ /**
+ * Called when GeofenceHardware object becomes available.
+ *
+ * @param geofenceHardware Geofence Hardware object. This can be null
+ * when for some reason the service connection gets disconnected.
+ */
+ public abstract void onGeofenceHardwareChange(GeofenceHardware geofenceHardware);
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 773d7b6..637ac85 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3534,6 +3534,9 @@
onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
(RccPlaybackState)msg.obj /* newState */);
break;
+ case MSG_RCC_SEEK_REQUEST:
+ onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
+ ((Long)msg.obj).longValue() /* timeMs */);
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -5867,7 +5870,16 @@
}
public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_QUEUE, generationId /* arg1 */,
+ // ignore position change requests if invalid generation ID
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClientGen != generationId) {
+ return;
+ }
+ }
+ }
+ // discard any unprocessed seek request in the message queue, and replace with latest
+ sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 795c3c2..61c55a5 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -767,10 +767,19 @@
boolean wantScan = false;
boolean blockScan = false;
WifiDisplay[] oldDisplays = oldStatus != null ?
- oldStatus.getRememberedDisplays() : new WifiDisplay[0];
- WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
- WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays();
- WifiDisplay activeDisplay = newStatus.getActiveDisplay();
+ oldStatus.getRememberedDisplays() : WifiDisplay.EMPTY_ARRAY;
+ WifiDisplay[] newDisplays;
+ WifiDisplay[] availableDisplays;
+ WifiDisplay activeDisplay;
+
+ if (newStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
+ newDisplays = newStatus.getRememberedDisplays();
+ availableDisplays = newStatus.getAvailableDisplays();
+ activeDisplay = newStatus.getActiveDisplay();
+ } else {
+ newDisplays = availableDisplays = WifiDisplay.EMPTY_ARRAY;
+ activeDisplay = null;
+ }
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index e076ef0..93bcf03 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -683,9 +683,12 @@
/**
* Called on the implementer to notify it that the playback head should be set at the given
* position. If the position can be changed from its current value, the implementor of
- * the interface should also update the playback position using
+ * the interface must also update the playback position using
* {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
* position being used, regardless of whether it differs from the requested position.
+ * Failure to do so would cause the system to not know the new actual playback position,
+ * and user interface components would fail to show the user where playback resumed after
+ * the position was updated.
* @param newPositionMs the new requested position in the current media, expressed in ms.
*/
void onPlaybackPositionUpdate(long newPositionMs);
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 6873060..416a2a1 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -28,6 +28,7 @@
libmedia \
libskia \
libui \
+ liblog \
libcutils \
libgui \
libstagefright \
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index b5d8b7b..3b1fb19 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -6,6 +6,7 @@
android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 040d2ab..6be7fdd 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -52,6 +52,7 @@
libaudioutils \
libbinder \
libcutils \
+ liblog \
libdl \
libgui \
libmedia \
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 9b11bfa..5835b9f 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -5,6 +5,7 @@
android_media_SoundPool.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/libdrm/mobile1/Android.mk b/media/libdrm/mobile1/Android.mk
index b07d91c..7356f46 100644
--- a/media/libdrm/mobile1/Android.mk
+++ b/media/libdrm/mobile1/Android.mk
@@ -44,6 +44,7 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils \
+ liblog \
libcrypto
LOCAL_MODULE := libdrm1
@@ -69,12 +70,13 @@
$(LOCAL_PATH)/include/parser \
$(JNI_H_INCLUDE) \
$(call include-path-for, system-core)/cutils
-
+
LOCAL_SHARED_LIBRARIES := libdrm1 \
libnativehelper \
libutils \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE := libdrm1_jni
diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk
index 1d69799..2a9448d 100644
--- a/media/mca/filterfw/Android.mk
+++ b/media/mca/filterfw/Android.mk
@@ -37,6 +37,7 @@
libdl \
libcutils \
libutils \
+ liblog \
libandroid \
libjnigraphics \
libmedia
@@ -48,5 +49,3 @@
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/mca/filterpacks/Android.mk b/media/mca/filterpacks/Android.mk
index 6166b1e..6e54f60 100644
--- a/media/mca/filterpacks/Android.mk
+++ b/media/mca/filterpacks/Android.mk
@@ -46,10 +46,8 @@
native/imageproc/invert.c \
native/imageproc/to_rgba.c
-LOCAL_SHARED_LIBRARIES := libutils libfilterfw
+LOCAL_SHARED_LIBRARIES := liblog libutils libfilterfw
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index 9dcc7ba..ad874c8 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -29,6 +29,7 @@
libstagefright_foundation \
libbinder \
libutils \
+ liblog \
libjpeg
LOCAL_C_INCLUDES := \
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
index c655ae6..adf0d30 100644
--- a/media/tests/players/Android.mk
+++ b/media/tests/players/Android.mk
@@ -20,7 +20,8 @@
LOCAL_SHARED_LIBRARIES:= \
libbinder \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE:= invoke_mock_media_player
LOCAL_MODULE_TAGS := tests eng
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 00d11da..207cc4b 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -17,6 +17,7 @@
storage_manager.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libandroidfw \
libutils \
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 5a2e261..ab7ceb6 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@
import java.io.Writer;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -30,10 +29,13 @@
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.os.SystemProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -164,11 +166,26 @@
private final static String TAG = "GLSurfaceView";
private final static boolean LOG_ATTACH_DETACH = false;
private final static boolean LOG_THREADS = false;
- private final static boolean LOG_PAUSE_RESUME = false;
private final static boolean LOG_SURFACE = false;
private final static boolean LOG_RENDERER = false;
private final static boolean LOG_RENDERER_DRAW_FRAME = false;
private final static boolean LOG_EGL = false;
+ private final static boolean TRACE_ENABLED = false;
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+ private int mUserRenderMode;
+
/**
* The renderer only renders
* when the surface is created, or when {@link #requestRender} is called.
@@ -241,13 +258,7 @@
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
- // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
- // this statement if back-porting to 2.2 or older:
- // holder.setFormat(PixelFormat.RGB_565);
- //
- // setType is not needed for SDK 2.0 or newer. Uncomment this
- // statement if back-porting this code to older SDKs.
- // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ mUserRenderMode = RENDERMODE_CONTINUOUSLY;
}
/**
@@ -346,15 +357,16 @@
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
- mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true, mEGLContextClientVersion);
}
if (mEGLContextFactory == null) {
- mEGLContextFactory = new DefaultContextFactory();
+ mEGLContextFactory = new DefaultContextFactory(mEGLContextClientVersion);
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
+
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
@@ -420,7 +432,7 @@
* @param needDepth
*/
public void setEGLConfigChooser(boolean needDepth) {
- setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth, mEGLContextClientVersion));
}
/**
@@ -439,7 +451,7 @@
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
- blueSize, alphaSize, depthSize, stencilSize));
+ blueSize, alphaSize, depthSize, stencilSize, mEGLContextClientVersion));
}
/**
@@ -466,6 +478,13 @@
* If
* {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
* EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ *
+ * This method must be called before:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ *
* @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
*/
public void setEGLContextClientVersion(int version) {
@@ -490,6 +509,14 @@
* @see #RENDERMODE_WHEN_DIRTY
*/
public void setRenderMode(int renderMode) {
+ switch (renderMode) {
+ case RENDERMODE_WHEN_DIRTY:
+ case RENDERMODE_CONTINUOUSLY:
+ break;
+ default:
+ throw new IllegalArgumentException("renderMode");
+ }
+ mUserRenderMode = renderMode;
mGLThread.setRenderMode(renderMode);
}
@@ -501,7 +528,7 @@
* @see #RENDERMODE_WHEN_DIRTY
*/
public int getRenderMode() {
- return mGLThread.getRenderMode();
+ return mUserRenderMode;
}
/**
@@ -582,14 +609,8 @@
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
if (mDetached && (mRenderer != null)) {
- int renderMode = RENDERMODE_CONTINUOUSLY;
- if (mGLThread != null) {
- renderMode = mGLThread.getRenderMode();
- }
mGLThread = new GLThread(mThisWeakRef);
- if (renderMode != RENDERMODE_CONTINUOUSLY) {
- mGLThread.setRenderMode(renderMode);
- }
+ mGLThread.setRenderMode(mUserRenderMode);
mGLThread.start();
}
mDetached = false;
@@ -761,11 +782,15 @@
void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}
- private class DefaultContextFactory implements EGLContextFactory {
- private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private static class DefaultContextFactory implements EGLContextFactory {
+ private final int mEGLContextClientVersion;
+
+ public DefaultContextFactory(int version) {
+ mEGLContextClientVersion = version;
+ }
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
- int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ int[] attrib_list = {EGL14.EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
@@ -775,9 +800,9 @@
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
- Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ Log.e(TAG, "display:" + display + " context: " + context);
if (LOG_THREADS) {
- Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "tid=" + Thread.currentThread().getId());
}
EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
}
@@ -807,8 +832,8 @@
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
- // This exception indicates that the surface flinger surface
- // is not valid. This can happen if the surface flinger surface has
+ // This exception indicates that the surfaceflinger surface
+ // is not valid. This can happen if the surfaceflinger surface has
// been torn down, but the application has not yet been
// notified via SurfaceHolder.Callback.surfaceDestroyed.
// In theory the application should be notified first,
@@ -844,10 +869,11 @@
EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}
- private abstract class BaseConfigChooser
+ private static abstract class BaseConfigChooser
implements EGLConfigChooser {
- public BaseConfigChooser(int[] configSpec) {
- mConfigSpec = filterConfigSpec(configSpec);
+
+ public BaseConfigChooser(int[] configSpec, int version) {
+ mConfigSpec = filterConfigSpec(configSpec, version);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
@@ -881,8 +907,8 @@
protected int[] mConfigSpec;
- private int[] filterConfigSpec(int[] configSpec) {
- if (mEGLContextClientVersion != 2) {
+ private int[] filterConfigSpec(int[] configSpec, int version) {
+ if (version != 2) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -892,7 +918,7 @@
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
@@ -902,9 +928,9 @@
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
- private class ComponentSizeChooser extends BaseConfigChooser {
+ private static class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
- int alphaSize, int depthSize, int stencilSize) {
+ int alphaSize, int depthSize, int stencilSize, int version) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
@@ -912,7 +938,7 @@
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
- EGL10.EGL_NONE});
+ EGL10.EGL_NONE}, version);
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
@@ -920,7 +946,7 @@
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
- }
+ }
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
@@ -931,14 +957,10 @@
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
+ int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
@@ -965,16 +987,16 @@
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
- }
+ }
/**
* This class will choose a RGB_888 surface with
* or without a depth buffer.
*
*/
- private class SimpleEGLConfigChooser extends ComponentSizeChooser {
- public SimpleEGLConfigChooser(boolean withDepthBuffer) {
- super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer, int version) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0, version);
}
}
@@ -991,9 +1013,9 @@
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void start() {
+ public void initialize() {
if (LOG_EGL) {
- Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "initialize() tid=" + Thread.currentThread().getId());
}
/*
* Get an EGL instance
@@ -1034,7 +1056,7 @@
throwEglException("createContext");
}
if (LOG_EGL) {
- Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
@@ -1048,7 +1070,7 @@
*/
public boolean createSurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createSurface() tid=" + Thread.currentThread().getId());
}
/*
* Check preconditions.
@@ -1083,7 +1105,7 @@
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
- Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
@@ -1097,8 +1119,9 @@
* Could not make the context current, probably because the underlying
* SurfaceView surface has been destroyed.
*/
- logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
- return false;
+ logEglErrorAsWarning(TAG, "eglMakeCurrent", mEgl.eglGetError());
+ // we fall-through to "true" here because we do have a
+ // valid EGLSurface at this point.
}
return true;
@@ -1108,8 +1131,7 @@
* Create a GL object for the current EGL context.
* @return
*/
- GL createGL() {
-
+ public GL createGL() {
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
@@ -1145,7 +1167,7 @@
public void destroySurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "destroySurface() tid=" + Thread.currentThread().getId());
}
destroySurfaceImp();
}
@@ -1163,9 +1185,9 @@
}
}
- public void finish() {
+ public void terminate() {
if (LOG_EGL) {
- Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "terminate() tid=" + Thread.currentThread().getId());
}
if (mEglContext != null) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
@@ -1187,7 +1209,7 @@
public static void throwEglException(String function, int error) {
String message = formatEglError(function, error);
if (LOG_THREADS) {
- Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ Log.e(TAG, "throwEglException tid=" + Thread.currentThread().getId() + " "
+ message);
}
throw new RuntimeException(message);
@@ -1207,584 +1229,411 @@
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
-
}
/**
* A generic GL Thread. Takes care of initializing EGL and GL. Delegates
* to a Renderer instance to do the actual drawing. Can be configured to
* render continuously or on request.
- *
- * All potentially blocking synchronization is done through the
- * sGLThreadManager object. This avoids multiple-lock ordering issues.
- *
*/
- static class GLThread extends Thread {
- GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
- super();
+
+ static class GLThread extends HandlerThread {
+ // only accessed from GLThread
+ private GL10 mGLContext;
+ private int mWidth;
+ private int mHeight;
+ private boolean mSizeChanged;
+ // current render mode
+ private int mRenderMode;
+ // the EGLSurface exists but isn't working for some reason
+ private boolean mEglSurfaceIsBad;
+ // we have an EGLContext
+ private boolean mHaveEglContext;
+ // we have an EGLSurface
+ private boolean mHaveEglSurface;
+ // we have a Surface (i.e.: EGLNativeWindowType)
+ private boolean mHasSurface;
+ // activity is paused
+ private boolean mPaused;
+
+ // constants
+ private EglHelper mEglHelper;
+ private Handler mGLHandler;
+ private Choreographer mChoreographer;
+
+ /*
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private final WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ private final Runnable mExecuteDrawAction = new Runnable() {
+ private int mTraceVsyncCounter = 0;
+ @Override
+ public void run() {
+ if (TRACE_ENABLED) {
+ Trace.traceCounter(Trace.TRACE_TAG_GRAPHICS,
+ "GLSurfaceView VSYNC counter", (mTraceVsyncCounter++) & 0xf);
+ }
+ executeDraw();
+ }
+ };
+
+ public GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super("GLThread", android.os.Process.THREAD_PRIORITY_DISPLAY);
+ if (LOG_THREADS) {
+ Log.d(TAG, "*** Starting GLThread ***");
+ }
mWidth = 0;
mHeight = 0;
- mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
+ private void readyToRun() {
+ mChoreographer = Choreographer.getInstance();
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ // getLooper() blocks until the thread is running
+ Looper looper = getLooper();
+ mGLHandler = new Handler(looper);
+ // don't return until the GLThread state has been initialized
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ readyToRun();
+ }
+ }, 0);
+ }
+
@Override
public void run() {
- setName("GLThread " + getId());
- if (LOG_THREADS) {
- Log.i("GLThread", "starting tid=" + getId());
- }
-
try {
- guardedRun();
- } catch (InterruptedException e) {
- // fall thru and exit normally
+ super.run();
} finally {
- sGLThreadManager.threadExiting(this);
+ // by definition the GLThread is not running anymore here
+ stopEglContext();
+ stopEglSurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglSurfaceLocked() {
+ // only call from the GLThread
+ private void stopEglSurface() {
if (mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL surface because paused tid=" + getId());
+ }
mHaveEglSurface = false;
mEglHelper.destroySurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglContextLocked() {
+ // only call from the GLThread
+ private void stopEglContext() {
if (mHaveEglContext) {
- mEglHelper.finish();
+ mEglHelper.terminate();
mHaveEglContext = false;
- sGLThreadManager.releaseEglContextLocked(this);
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL context because paused tid=" + getId());
+ }
}
}
- private void guardedRun() throws InterruptedException {
- mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
- mHaveEglContext = false;
- mHaveEglSurface = false;
- try {
- GL10 gl = null;
- boolean createEglContext = false;
- boolean createEglSurface = false;
- boolean createGlInterface = false;
- boolean lostEglContext = false;
- boolean sizeChanged = false;
- boolean wantRenderNotification = false;
- boolean doRenderNotification = false;
- boolean askedToReleaseEglContext = false;
- int w = 0;
- int h = 0;
- Runnable event = null;
- while (true) {
- synchronized (sGLThreadManager) {
- while (true) {
- if (mShouldExit) {
- return;
- }
+ private void updateState() {
+ final boolean wasAbleToDraw = isAbleToDraw();
+ if (!isReadyToDraw()) {
+ return;
+ }
- if (! mEventQueue.isEmpty()) {
- event = mEventQueue.remove(0);
- break;
- }
+ if (!mHaveEglSurface || mSizeChanged) {
+ // create EGL context if needed
+ boolean reportSurfaceCreated = false;
+ if (!mHaveEglContext) {
+ mEglHelper.initialize();
+ mHaveEglContext = true;
+ reportSurfaceCreated = true;
+ }
- // Update the pause state.
- boolean pausing = false;
- if (mPaused != mRequestPaused) {
- pausing = mRequestPaused;
- mPaused = mRequestPaused;
- sGLThreadManager.notifyAll();
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
- }
- }
+ // get the GL interface for the active EGLContext
+ mGLContext = (GL10)mEglHelper.createGL();
- // Do we need to give up the EGL context?
- if (mShouldReleaseEglContext) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
- }
- stopEglSurfaceLocked();
- stopEglContextLocked();
- mShouldReleaseEglContext = false;
- askedToReleaseEglContext = true;
- }
+ // create EGL Surface
+ mHaveEglSurface = mEglHelper.createSurface();
+ mEglSurfaceIsBad = !mHaveEglSurface;
+ mSizeChanged = false;
- // Have we lost the EGL context?
- if (lostEglContext) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
- lostEglContext = false;
- }
-
- // When pausing, release the EGL surface:
- if (pausing && mHaveEglSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
- }
- stopEglSurfaceLocked();
- }
-
- // When pausing, optionally release the EGL Context:
- if (pausing && mHaveEglContext) {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- boolean preserveEglContextOnPause = view == null ?
- false : view.mPreserveEGLContextOnPause;
- if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
- stopEglContextLocked();
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
- }
- }
- }
-
- // When pausing, optionally terminate EGL:
- if (pausing) {
- if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
- mEglHelper.finish();
- if (LOG_SURFACE) {
- Log.i("GLThread", "terminating EGL because paused tid=" + getId());
- }
- }
- }
-
- // Have we lost the SurfaceView surface?
- if ((! mHasSurface) && (! mWaitingForSurface)) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
- }
- if (mHaveEglSurface) {
- stopEglSurfaceLocked();
- }
- mWaitingForSurface = true;
- mSurfaceIsBad = false;
- sGLThreadManager.notifyAll();
- }
-
- // Have we acquired the surface view surface?
- if (mHasSurface && mWaitingForSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
- }
- mWaitingForSurface = false;
- sGLThreadManager.notifyAll();
- }
-
- if (doRenderNotification) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "sending render notification tid=" + getId());
- }
- wantRenderNotification = false;
- doRenderNotification = false;
- mRenderComplete = true;
- sGLThreadManager.notifyAll();
- }
-
- // Ready to draw?
- if (readyToDraw()) {
-
- // If we don't have an EGL context, try to acquire one.
- if (! mHaveEglContext) {
- if (askedToReleaseEglContext) {
- askedToReleaseEglContext = false;
- } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
- try {
- mEglHelper.start();
- } catch (RuntimeException t) {
- sGLThreadManager.releaseEglContextLocked(this);
- throw t;
- }
- mHaveEglContext = true;
- createEglContext = true;
-
- sGLThreadManager.notifyAll();
- }
- }
-
- if (mHaveEglContext && !mHaveEglSurface) {
- mHaveEglSurface = true;
- createEglSurface = true;
- createGlInterface = true;
- sizeChanged = true;
- }
-
- if (mHaveEglSurface) {
- if (mSizeChanged) {
- sizeChanged = true;
- w = mWidth;
- h = mHeight;
- wantRenderNotification = true;
- if (LOG_SURFACE) {
- Log.i("GLThread",
- "noticing that we want render notification tid="
- + getId());
- }
-
- // Destroy and recreate the EGL surface.
- createEglSurface = true;
-
- mSizeChanged = false;
- }
- mRequestRender = false;
- sGLThreadManager.notifyAll();
- break;
- }
- }
-
- // By design, this is the only place in a GLThread thread where we wait().
- if (LOG_THREADS) {
- Log.i("GLThread", "waiting tid=" + getId()
- + " mHaveEglContext: " + mHaveEglContext
- + " mHaveEglSurface: " + mHaveEglSurface
- + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
- + " mPaused: " + mPaused
- + " mHasSurface: " + mHasSurface
- + " mSurfaceIsBad: " + mSurfaceIsBad
- + " mWaitingForSurface: " + mWaitingForSurface
- + " mWidth: " + mWidth
- + " mHeight: " + mHeight
- + " mRequestRender: " + mRequestRender
- + " mRenderMode: " + mRenderMode);
- }
- sGLThreadManager.wait();
- }
- } // end of synchronized(sGLThreadManager)
-
- if (event != null) {
- event.run();
- event = null;
- continue;
- }
-
- if (createEglSurface) {
- if (LOG_SURFACE) {
- Log.w("GLThread", "egl createSurface");
- }
- if (mEglHelper.createSurface()) {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- sGLThreadManager.notifyAll();
- }
- } else {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
- continue;
- }
- createEglSurface = false;
- }
-
- if (createGlInterface) {
- gl = (GL10) mEglHelper.createGL();
-
- sGLThreadManager.checkGLDriver(gl);
- createGlInterface = false;
- }
-
- if (createEglContext) {
+ // notify use of surface size change
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (reportSurfaceCreated) {
if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceCreated");
+ Log.d(TAG, "onSurfaceCreated");
}
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
- }
- createEglContext = false;
+ view.mRenderer.onSurfaceCreated(mGLContext, mEglHelper.mEglConfig);
}
- if (sizeChanged) {
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
- }
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceChanged(gl, w, h);
- }
- sizeChanged = false;
+ if (LOG_RENDERER) {
+ Log.d(TAG, "onSurfaceChanged(" + mWidth + ", " + mHeight + ")");
}
+ view.mRenderer.onSurfaceChanged(mGLContext, mWidth, mHeight);
+ }
+ }
- if (LOG_RENDERER_DRAW_FRAME) {
- Log.w("GLThread", "onDrawFrame tid=" + getId());
- }
- {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onDrawFrame(gl);
- }
- }
+ // see if we should kick the rendering loop
+ if (!wasAbleToDraw && isAbleToDraw()) {
+ // we're now able to draw
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
+ }
+
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.d(TAG, "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mEglSurfaceIsBad
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRenderMode: " + mRenderMode);
+ }
+ }
+
+ private void executeDraw() {
+ if (TRACE_ENABLED) {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "executeDraw");
+ }
+
+ if (isAbleToDraw()) {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
+
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.d(TAG, "onDrawFrame tid=" + getId());
+ }
+
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onDrawFrame(mGLContext);
int swapError = mEglHelper.swap();
switch (swapError) {
case EGL10.EGL_SUCCESS:
break;
case EGL11.EGL_CONTEXT_LOST:
if (LOG_SURFACE) {
- Log.i("GLThread", "egl context lost tid=" + getId());
+ Log.d(TAG, "egl context lost tid=" + getId());
}
- lostEglContext = true;
+ stopEglSurface();
+ stopEglContext();
break;
default:
// Other errors typically mean that the current surface is bad,
// probably because the SurfaceView surface has been destroyed,
// but we haven't been notified yet.
// Log the error to help developers understand why rendering stopped.
- EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
-
- synchronized(sGLThreadManager) {
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
+ EglHelper.logEglErrorAsWarning(TAG, "eglSwapBuffers", swapError);
+ mEglSurfaceIsBad = true;
break;
}
-
- if (wantRenderNotification) {
- doRenderNotification = true;
- }
}
+ }
- } finally {
- /*
- * clean-up everything...
- */
- synchronized (sGLThreadManager) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
+ if (TRACE_ENABLED) {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ }
+ }
+
+ private boolean isAbleToDraw() {
+ return mHaveEglContext && mHaveEglSurface && isReadyToDraw();
+ }
+
+ private boolean isReadyToDraw() {
+ return (!mPaused) && mHasSurface && (!mEglSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0);
+ }
+
+ private boolean isEglContextReleasedWhenPausing() {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ return (view != null) ? !view.mPreserveEGLContextOnPause : false;
+ }
+
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("Runnable r must not be null");
+ }
+ mGLHandler.post(r);
+ }
+
+ /*
+ * the call-backs below all run on the GLThread and implement state
+ * changes of the GLSurfaceView and Activity life cycle.
+ */
+
+ private void doSurfaceCreated() {
+ mHasSurface = true;
+ updateState();
+ }
+
+ private void doSurfaceDestroyed() {
+ if (mHasSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "noticed surfaceView surface lost tid=" + getId());
+ }
+ stopEglSurface();
+ }
+ mHasSurface = false;
+ }
+
+ private void doPause() {
+ if (mPaused == false) {
+ mPaused = true;
+ stopEglSurface();
+ // When pausing, optionally release the EGL Context:
+ if (mHaveEglContext && isEglContextReleasedWhenPausing()) {
+ stopEglContext();
}
}
}
- public boolean ableToDraw() {
- return mHaveEglContext && mHaveEglSurface && readyToDraw();
- }
-
- private boolean readyToDraw() {
- return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
- && (mWidth > 0) && (mHeight > 0)
- && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
- }
-
- public void setRenderMode(int renderMode) {
- if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
- throw new IllegalArgumentException("renderMode");
- }
- synchronized(sGLThreadManager) {
- mRenderMode = renderMode;
- sGLThreadManager.notifyAll();
+ private void doResume() {
+ mPaused = false;
+ updateState();
+ if (mRenderMode == RENDERMODE_WHEN_DIRTY) {
+ requestRender();
}
}
- public int getRenderMode() {
- synchronized(sGLThreadManager) {
- return mRenderMode;
- }
+ private void doWindowResize(final int width, final int height) {
+ // we were not drawing yet. Update the window size and
+ // state and attempt to draw a frame.
+ mSizeChanged = (mWidth != width || mHeight != height);
+ mWidth = width;
+ mHeight = height;
+ updateState();
+ // we always (attempt to) draw a frame before returning
+ executeDraw();
}
- public void requestRender() {
- synchronized(sGLThreadManager) {
- mRequestRender = true;
- sGLThreadManager.notifyAll();
- }
+ private void doSetRenderMode(final int renderMode) {
+ mRenderMode = renderMode;
+ requestRender();
}
+ /*
+ * the call-backs below run on the main UI thread, they just
+ * wait while executing work on the GLThread.
+ */
+
public void surfaceCreated() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceCreated tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceCreated();
}
- mHasSurface = true;
- mFinishedCreatingEglSurface = false;
- sGLThreadManager.notifyAll();
- while (mWaitingForSurface
- && !mFinishedCreatingEglSurface
- && !mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void surfaceDestroyed() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceDestroyed();
}
- mHasSurface = false;
- sGLThreadManager.notifyAll();
- while((!mWaitingForSurface) && (!mExited)) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onPause() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onPause tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doPause();
}
- mRequestPaused = true;
- sGLThreadManager.notifyAll();
- while ((! mExited) && (! mPaused)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onPause waiting for mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onResume() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onResume tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doResume();
}
- mRequestPaused = false;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
- while ((! mExited) && mPaused && (!mRenderComplete)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onResume waiting for !mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onWindowResize(int w, int h) {
- synchronized (sGLThreadManager) {
- mWidth = w;
- mHeight = h;
- mSizeChanged = true;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
-
- // Wait for thread to react to resize and render a frame
- while (! mExited && !mPaused && !mRenderComplete
- && ableToDraw()) {
- if (LOG_SURFACE) {
- Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
+ final int width = w;
+ final int height = h;
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doWindowResize(width, height);
}
+ }, 0);
+ }
+
+ /*
+ * the methods below can be called from any thread
+ */
+
+ public void requestRender() {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
+ mExecuteDrawAction, null);
+ } else {
+ /*
+ * in RENDERMODE_WHEN_DIRTY we schedule the draw callback
+ * immediately because the developer is manager her
+ * timing loop manually -- in particular she could be
+ * using the Choreographer already.
+ */
+ mGLHandler.post(mExecuteDrawAction);
}
}
+ public void setRenderMode(final int renderMode) {
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSetRenderMode(renderMode);
+ }
+ }, 0);
+ }
+
public void requestExitAndWait() {
- // don't call this from GLThread thread or it is a guaranteed
- // deadlock!
- synchronized(sGLThreadManager) {
- mShouldExit = true;
- sGLThreadManager.notifyAll();
- while (! mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
+ getLooper().quit();
+ try {
+ this.join();
+ } catch (InterruptedException e) {
}
}
-
- public void requestReleaseEglContextLocked() {
- mShouldReleaseEglContext = true;
- sGLThreadManager.notifyAll();
- }
-
- /**
- * Queue an "event" to be run on the GL rendering thread.
- * @param r the runnable to be run on the GL rendering thread.
- */
- public void queueEvent(Runnable r) {
- if (r == null) {
- throw new IllegalArgumentException("r must not be null");
- }
- synchronized(sGLThreadManager) {
- mEventQueue.add(r);
- sGLThreadManager.notifyAll();
- }
- }
-
- // Once the thread is started, all accesses to the following member
- // variables are protected by the sGLThreadManager monitor
- private boolean mShouldExit;
- private boolean mExited;
- private boolean mRequestPaused;
- private boolean mPaused;
- private boolean mHasSurface;
- private boolean mSurfaceIsBad;
- private boolean mWaitingForSurface;
- private boolean mHaveEglContext;
- private boolean mHaveEglSurface;
- private boolean mFinishedCreatingEglSurface;
- private boolean mShouldReleaseEglContext;
- private int mWidth;
- private int mHeight;
- private int mRenderMode;
- private boolean mRequestRender;
- private boolean mRenderComplete;
- private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
- private boolean mSizeChanged = true;
-
- // End of member variables protected by the sGLThreadManager monitor.
-
- private EglHelper mEglHelper;
-
- /**
- * Set once at thread construction time, nulled out when the parent view is garbage
- * called. This weak reference allows the GLSurfaceView to be garbage collected while
- * the GLThread is still alive.
- */
- private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
-
- }
+ } // class GLThread
static class LogWriter extends Writer {
-
- @Override public void close() {
+ @Override
+ public void close() {
flushBuilder();
}
- @Override public void flush() {
+ @Override
+ public void flush() {
flushBuilder();
}
- @Override public void write(char[] buf, int offset, int count) {
- for(int i = 0; i < count; i++) {
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ for (int i = 0; i < count; i++) {
char c = buf[offset + i];
- if ( c == '\n') {
+ if (c == '\n') {
flushBuilder();
- }
- else {
+ } else {
mBuilder.append(c);
}
}
@@ -1792,7 +1641,7 @@
private void flushBuilder() {
if (mBuilder.length() > 0) {
- Log.v("GLSurfaceView", mBuilder.toString());
+ Log.v(TAG, mBuilder.toString());
mBuilder.delete(0, mBuilder.length());
}
}
@@ -1800,141 +1649,10 @@
private StringBuilder mBuilder = new StringBuilder();
}
-
private void checkRenderThreadState() {
if (mGLThread != null) {
throw new IllegalStateException(
"setRenderer has already been called for this instance.");
}
}
-
- private static class GLThreadManager {
- private static String TAG = "GLThreadManager";
-
- public synchronized void threadExiting(GLThread thread) {
- if (LOG_THREADS) {
- Log.i("GLThread", "exiting tid=" + thread.getId());
- }
- thread.mExited = true;
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- /*
- * Tries once to acquire the right to use an EGL
- * context. Does not block. Requires that we are already
- * in the sGLThreadManager monitor when this is called.
- *
- * @return true if the right to use an EGL context was acquired.
- */
- public boolean tryAcquireEglContextLocked(GLThread thread) {
- if (mEglOwner == thread || mEglOwner == null) {
- mEglOwner = thread;
- notifyAll();
- return true;
- }
- checkGLESVersion();
- if (mMultipleGLESContextsAllowed) {
- return true;
- }
- // Notify the owning thread that it should release the context.
- // TODO: implement a fairness policy. Currently
- // if the owning thread is drawing continuously it will just
- // reacquire the EGL context.
- if (mEglOwner != null) {
- mEglOwner.requestReleaseEglContextLocked();
- }
- return false;
- }
-
- /*
- * Releases the EGL context. Requires that we are already in the
- * sGLThreadManager monitor when this is called.
- */
- public void releaseEglContextLocked(GLThread thread) {
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- public synchronized boolean shouldReleaseEGLContextWhenPausing() {
- // Release the EGL context when pausing even if
- // the hardware supports multiple EGL contexts.
- // Otherwise the device could run out of EGL contexts.
- return mLimitedGLESContexts;
- }
-
- public synchronized boolean shouldTerminateEGLWhenPausing() {
- checkGLESVersion();
- return !mMultipleGLESContextsAllowed;
- }
-
- public synchronized void checkGLDriver(GL10 gl) {
- if (! mGLESDriverCheckComplete) {
- checkGLESVersion();
- String renderer = gl.glGetString(GL10.GL_RENDERER);
- if (mGLESVersion < kGLES_20) {
- mMultipleGLESContextsAllowed =
- ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
- notifyAll();
- }
- mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
- + mMultipleGLESContextsAllowed
- + " mLimitedGLESContexts = " + mLimitedGLESContexts);
- }
- mGLESDriverCheckComplete = true;
- }
- }
-
- private void checkGLESVersion() {
- if (! mGLESVersionCheckComplete) {
- mGLESVersion = SystemProperties.getInt(
- "ro.opengles.version",
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
- if (mGLESVersion >= kGLES_20) {
- mMultipleGLESContextsAllowed = true;
- }
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLESVersion mGLESVersion =" +
- " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
- }
- mGLESVersionCheckComplete = true;
- }
- }
-
- /**
- * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
- * support for hardware-accelerated views, therefore multiple EGL contexts are
- * supported on all Android 3.0+ EGL drivers.
- */
- private boolean mGLESVersionCheckComplete;
- private int mGLESVersion;
- private boolean mGLESDriverCheckComplete;
- private boolean mMultipleGLESContextsAllowed;
- private boolean mLimitedGLESContexts;
- private static final int kGLES_20 = 0x20000;
- private static final String kMSM7K_RENDERER_PREFIX =
- "Q3Dimension MSM7500 ";
- private GLThread mEglOwner;
- }
-
- private static final GLThreadManager sGLThreadManager = new GLThreadManager();
-
- private final WeakReference<GLSurfaceView> mThisWeakRef =
- new WeakReference<GLSurfaceView>(this);
- private GLThread mGLThread;
- private Renderer mRenderer;
- private boolean mDetached;
- private EGLConfigChooser mEGLConfigChooser;
- private EGLContextFactory mEGLContextFactory;
- private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
- private GLWrapper mGLWrapper;
- private int mDebugFlags;
- private int mEGLContextClientVersion;
- private boolean mPreserveEGLContextOnPause;
}
diff --git a/packages/DefaultContainerService/jni/Android.mk b/packages/DefaultContainerService/jni/Android.mk
index 79ff451..ef4f699 100644
--- a/packages/DefaultContainerService/jni/Android.mk
+++ b/packages/DefaultContainerService/jni/Android.mk
@@ -28,7 +28,8 @@
LOCAL_SHARED_LIBRARIES := \
libnativehelper \
- libutils
+ libutils \
+ liblog
LOCAL_STATIC_LIBRARIES := \
libdiskusage
@@ -36,4 +37,4 @@
LOCAL_MODULE := libdefcontainer_jni
LOCAL_MODULE_TAGS := optional
-include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk
index 0606ab4..75eb528 100644
--- a/services/common_time/Android.mk
+++ b/services/common_time/Android.mk
@@ -27,7 +27,8 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcommon_time_client \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := common_time
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 159800f..5d913f3 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -29,6 +29,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 0773afb..376de96 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -40,7 +40,6 @@
#include <androidfw/KeyCharacterMap.h>
#include <androidfw/VirtualKeyMap.h>
-#include <sha1.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
@@ -49,6 +48,7 @@
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
+#include <sys/sha1.h>
/* this macro is used to tell if "bit" is set in "array"
* it selects a byte from the array, and does a boolean AND
@@ -162,7 +162,8 @@
next(NULL),
fd(fd), id(id), path(path), identifier(identifier),
classes(0), configuration(NULL), virtualKeyMap(NULL),
- ffEffectPlaying(false), ffEffectId(-1) {
+ ffEffectPlaying(false), ffEffectId(-1),
+ timestampOverrideSec(0), timestampOverrideUsec(0) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
@@ -766,12 +767,37 @@
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
- const struct input_event& iev = readBuffer[i];
- ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+ struct input_event& iev = readBuffer[i];
+ ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
+ // Some input devices may have a better concept of the time
+ // when an input event was actually generated than the kernel
+ // which simply timestamps all events on entry to evdev.
+ // This is a custom Android extension of the input protocol
+ // mainly intended for use with uinput based device drivers.
+ if (iev.type == EV_MSC) {
+ if (iev.code == MSC_ANDROID_TIME_SEC) {
+ device->timestampOverrideSec = iev.value;
+ continue;
+ } else if (iev.code == MSC_ANDROID_TIME_USEC) {
+ device->timestampOverrideUsec = iev.value;
+ continue;
+ }
+ }
+ if (device->timestampOverrideSec || device->timestampOverrideUsec) {
+ iev.time.tv_sec = device->timestampOverrideSec;
+ iev.time.tv_usec = device->timestampOverrideUsec;
+ if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
+ device->timestampOverrideSec = 0;
+ device->timestampOverrideUsec = 0;
+ }
+ ALOGV("applied override time %d.%06d",
+ int(iev.time.tv_sec), int(iev.time.tv_usec));
+ }
+
#ifdef HAVE_POSIX_CLOCKS
// Use the time specified in the event instead of the current time
// so that downstream code can get more accurate estimates of
@@ -829,8 +855,8 @@
event->code = iev.code;
event->value = iev.value;
event += 1;
+ capacity -= 1;
}
- capacity -= count;
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
@@ -1162,11 +1188,6 @@
mBuiltInKeyboardId = device->id;
}
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
- }
-
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -1184,6 +1205,14 @@
}
}
+ // 'Q' key support = cheap test of whether this is an alpha-capable kbd. Many gamepads will
+ // report a broader set of HID usages than they need, however, so we only want to mark this
+ // device as a keyboard if it is not a gamepad.
+ if (hasKeycodeLocked(device, AKEYCODE_Q) &&
+ !(device->classes & INPUT_DEVICE_CLASS_GAMEPAD)) {
+ device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+ }
+
// Disable kernel key repeat since we handle it ourselves
unsigned int repeatRate[] = {0,0};
if (ioctl(fd, EVIOCSREP, repeatRate)) {
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index afc12ef..c93fc7a 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -42,6 +42,20 @@
#define BTN_FIRST 0x100 // first button code
#define BTN_LAST 0x15f // last button code
+/*
+ * These constants are used privately in Android to pass raw timestamps
+ * through evdev from uinput device drivers because there is currently no
+ * other way to transfer this information. The evdev driver automatically
+ * timestamps all input events with the time they were posted and clobbers
+ * whatever information was passed in.
+ *
+ * For the purposes of this hack, the timestamp is specified in the
+ * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
+ * seconds and microseconds.
+ */
+#define MSC_ANDROID_TIME_SEC 0x6
+#define MSC_ANDROID_TIME_USEC 0x7
+
namespace android {
enum {
@@ -329,6 +343,9 @@
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
+ int32_t timestampOverrideSec;
+ int32_t timestampOverrideUsec;
+
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 602afd4..ab38ed2 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2701,6 +2701,12 @@
mPointerYZoomScale);
dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
mPointerGestureMaxSwipeWidth);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dump.appendFormat(INDENT3 "Navigation Gesture Detector:\n");
+ dump.appendFormat(INDENT4 "AssistStartY: %0.3f\n",
+ mNavigationAssistStartY);
+ dump.appendFormat(INDENT4 "AssistEndY: %0.3f\n",
+ mNavigationAssistEndY);
}
}
@@ -2895,7 +2901,7 @@
}
} else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_UNSCALED;
+ mDeviceMode = DEVICE_MODE_NAVIGATION;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DEVICE_MODE_UNSCALED;
@@ -3243,8 +3249,8 @@
break;
}
- // Compute pointer gesture detection parameters.
if (mDeviceMode == DEVICE_MODE_POINTER) {
+ // Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
@@ -3269,10 +3275,14 @@
// translated into freeform gestures.
mPointerGestureMaxSwipeWidth =
mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
- }
- // Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
+ // Abort current pointer usages because the state has changed.
+ abortPointerUsage(when, 0 /*policyFlags*/);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ // Compute navigation parameters.
+ mNavigationAssistStartY = mSurfaceHeight * 0.9f;
+ mNavigationAssistEndY = mSurfaceHeight * 0.5f;
+ }
// Inform the dispatcher about the changes.
*outResetNeeded = true;
@@ -3611,6 +3621,7 @@
mPointerGesture.reset();
mPointerSimple.reset();
+ mNavigation.reset();
if (mPointerController != NULL) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -3761,6 +3772,8 @@
mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
mCurrentCookedPointerData.touchingIdBits);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dispatchNavigationAssist(when, policyFlags);
}
dispatchHoverExit(when, policyFlags);
@@ -5482,6 +5495,44 @@
dispatchPointerSimple(when, policyFlags, false, false);
}
+void TouchInputMapper::dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags) {
+ if (mCurrentCookedPointerData.touchingIdBits.count() == 1) {
+ if (mLastCookedPointerData.touchingIdBits.isEmpty()) {
+ // First pointer down.
+ uint32_t id = mCurrentCookedPointerData.touchingIdBits.firstMarkedBit();
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(id);
+ if (coords.getY() >= mNavigationAssistStartY) {
+ // Start tracking the possible assist swipe.
+ mNavigation.activeAssistId = id;
+ return;
+ }
+ } else if (mNavigation.activeAssistId >= 0
+ && mCurrentCookedPointerData.touchingIdBits.hasBit(mNavigation.activeAssistId)) {
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(
+ mNavigation.activeAssistId);
+ if (coords.getY() > mNavigationAssistEndY) {
+ // Swipe is still in progress.
+ return;
+ }
+
+ // Detected assist swipe.
+ int32_t metaState = mContext->getGlobalMetaState();
+ NotifyKeyArgs downArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&downArgs);
+
+ NotifyKeyArgs upArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_UP, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&upArgs);
+ }
+ }
+
+ // Cancel the assist swipe.
+ mNavigation.activeAssistId = -1;
+}
+
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 8a52c06..312f19b 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -791,6 +791,10 @@
void clear();
void copyFrom(const CookedPointerData& other);
+ inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
+ return pointerCoords[idToIndex[id]];
+ }
+
inline bool isHovering(uint32_t pointerIndex) {
return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
}
@@ -1180,6 +1184,7 @@
DEVICE_MODE_DISABLED, // input is disabled
DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
+ DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
DEVICE_MODE_POINTER, // pointer mapping (pointer)
};
DeviceMode mDeviceMode;
@@ -1432,6 +1437,10 @@
// The maximum swipe width.
float mPointerGestureMaxSwipeWidth;
+ // The start and end Y thresholds for invoking the assist navigation swipe.
+ float mNavigationAssistStartY;
+ float mNavigationAssistEndY;
+
struct PointerDistanceHeapElement {
uint32_t currentPointerIndex : 8;
uint32_t lastPointerIndex : 8;
@@ -1606,6 +1615,15 @@
}
} mPointerSimple;
+ struct Navigation {
+ // The id of a pointer that is tracking a possible assist swipe.
+ int32_t activeAssistId; // -1 if none
+
+ void reset() {
+ activeAssistId = -1;
+ }
+ } mNavigation;
+
// The pointer and scroll velocity controls.
VelocityControl mPointerVelocityControl;
VelocityControl mWheelXVelocityControl;
@@ -1641,6 +1659,8 @@
bool down, bool hovering);
void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+ void dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags);
+
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 8f8c34b..211e64b 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -9,6 +9,7 @@
shared_libraries := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index bcb7cb7..1e1cf5a 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -54,19 +54,17 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
-
-import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
import com.android.server.location.LocationBlacklist;
@@ -338,11 +336,11 @@
addProviderLocked(passiveProvider);
mEnabledProviders.add(passiveProvider.getName());
mPassiveProvider = passiveProvider;
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
+ mLocationHandler.getLooper());
if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
- mLocationHandler.getLooper());
mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
addProviderLocked(gpsProvider);
@@ -406,6 +404,14 @@
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
+
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, providerPackageNames,
+ mLocationHandler, gpsProvider.getGpsGeofenceProxy());
+ if (provider == null) {
+ Slog.e(TAG, "no geofence provider found");
+ }
+
}
/**
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 3d2e912..0f1700d 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -47,6 +47,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -4600,11 +4601,13 @@
private final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
+ final Uri data = r.intent.getData();
+ final String strData = data != null ? data.toSafeString() : null;
+
EventLog.writeEvent(tag,
r.userId, System.identityHashCode(r), task.taskId,
r.shortComponentName, r.intent.getAction(),
- r.intent.getType(), r.intent.getDataString(),
- r.intent.getFlags());
+ r.intent.getType(), strData, r.intent.getFlags());
}
/**
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
new file mode 100644
index 0000000..36e9fcc
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.hardware.location.GeofenceHardwareService;
+import android.hardware.location.IGeofenceHardware;
+import android.location.IGeofenceProvider;
+import android.location.IGpsGeofenceHardware;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import com.android.server.ServiceWatcher;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class GeofenceProxy {
+ private static final String TAG = "GeofenceProxy";
+ private static final String SERVICE_ACTION =
+ "com.android.location.service.GeofenceProvider";
+ private ServiceWatcher mServiceWatcher;
+ private Context mContext;
+ private IGeofenceHardware mGeofenceHardware;
+ private IGpsGeofenceHardware mGpsGeofenceHardware;
+
+ private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
+ private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
+ private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;
+ private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;
+ private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;
+
+ private Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
+ }
+ };
+
+ public static GeofenceProxy createAndBind(Context context,
+ List<String> initialPackageNames, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, initialPackageNames, handler, gpsGeofence);
+ if (proxy.bindGeofenceProvider()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private GeofenceProxy(Context context, List<String> initialPackageName, Handler handler,
+ IGpsGeofenceHardware gpsGeofence) {
+ mContext = context;
+ mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, initialPackageName,
+ mRunnable, handler);
+ mGpsGeofenceHardware = gpsGeofence;
+ bindHardwareGeofence();
+ }
+
+ private boolean bindGeofenceProvider() {
+ return mServiceWatcher.start();
+ }
+
+ private IGeofenceProvider getGeofenceProviderService() {
+ return IGeofenceProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
+
+ private void bindHardwareGeofence() {
+ mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
+ mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mGeofenceHardware = null;
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
+ }
+ };
+
+ private void setGeofenceHardwareInProvider() {
+ try {
+ getGeofenceProviderService().setGeofenceHardware(mGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception: setGeofenceHardwareInProvider: " + e);
+ }
+ }
+
+ private void setGpsGeofence() {
+ try {
+ mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while connecting to GeofenceHardwareService");
+ }
+ }
+
+
+ // This needs to be reworked, when more services get added,
+ // Might need a state machine or add a framework utility class,
+ private Handler mHandler = new Handler() {
+ private boolean mGeofenceHardwareConnected = false;
+ private boolean mGeofenceProviderConnected = false;
+
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case GEOFENCE_PROVIDER_CONNECTED:
+ mGeofenceProviderConnected = true;
+ if (mGeofenceHardwareConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_CONNECTED:
+ setGpsGeofence();
+ mGeofenceHardwareConnected = true;
+ if (mGeofenceProviderConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_DISCONNECTED:
+ mGeofenceHardwareConnected = false;
+ setGeofenceHardwareInProvider();
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3552b6a..1ebff67 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -24,7 +24,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
+import android.hardware.location.GeofenceHardwareImpl;
+import android.hardware.location.IGeofenceHardware;
import android.location.Criteria;
+import android.location.IGpsGeofenceHardware;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
@@ -314,6 +317,8 @@
// only modified on handler thread
private WorkSource mClientSource = new WorkSource();
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
@Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
@@ -367,6 +372,10 @@
return mGpsStatusProvider;
}
+ public IGpsGeofenceHardware getGpsGeofenceProxy() {
+ return mGpsGeofenceBinder;
+ }
+
private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -918,6 +927,31 @@
return result;
}
+ private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
+ public boolean isHardwareGeofenceSupported() {
+ return native_is_geofence_supported();
+ }
+
+ public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition, int monitorTransitions,
+ int notificationResponsiveness, int unknownTimer) {
+ return native_add_geofence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
+ }
+
+ public boolean removeHardwareGeofence(int geofenceId) {
+ return native_remove_geofence(geofenceId);
+ }
+
+ public boolean pauseHardwareGeofence(int geofenceId) {
+ return native_pause_geofence(geofenceId);
+ }
+
+ public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
+ return native_resume_geofence(geofenceId, monitorTransition);
+ }
+ };
+
private boolean deleteAidingData(Bundle extras) {
int flags;
@@ -1017,6 +1051,7 @@
return ((mEngineCapabilities & capability) != 0);
}
+
/**
* called from native code to update our position.
*/
@@ -1320,6 +1355,73 @@
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
}
+ /**
+ * Called from native to report GPS Geofence transition
+ * All geofence callbacks are called on the same thread
+ */
+ private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude,
+ altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp);
+ }
+
+ /**
+ * called from native code to report GPS status change.
+ */
+ private void reportGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude,
+ speed, bearing, accuracy, timestamp);
+ }
+
+ /**
+ * called from native code - Geofence Add callback
+ */
+ private void reportGeofenceAddStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Remove callback
+ */
+ private void reportGeofenceRemoveStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Pause callback
+ */
+ private void reportGeofencePauseStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Resume callback
+ */
+ private void reportGeofenceResumeStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status);
+ }
+
//=============================================================
// NI Client support
//=============================================================
@@ -1650,4 +1752,13 @@
private native void native_update_network_state(boolean connected, int type,
boolean roaming, boolean available, String extraInfo, String defaultAPN);
+
+ // Hardware Geofence support.
+ private static native boolean native_is_geofence_supported();
+ private static native boolean native_add_geofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition,int monitorTransitions,
+ int notificationResponsivenes, int unknownTimer);
+ private static native boolean native_remove_geofence(int geofenceId);
+ private static native boolean native_resume_geofence(int geofenceId, int transitions);
+ private static native boolean native_pause_geofence(int geofenceId);
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index cc9b785..154c4e8 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -8249,6 +8249,24 @@
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
+ // For system-bundled packages, we assume that installing an upgraded version
+ // of the package implies that the user actually wants to run that new code,
+ // so we enable the package.
+ if (isSystemApp(newPackage)) {
+ // NB: implicit assumption that system package upgrades apply to all users
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
+ }
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ if (res.origUsers != null) {
+ for (int userHandle : res.origUsers) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ userHandle, installerPackageName);
+ }
+ }
+ }
+ }
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 297324b..6293dc6 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -4,6 +4,7 @@
import android.graphics.Matrix;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -30,6 +31,11 @@
// Protect with mAnimator.
boolean freezingScreen;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int lastFreezeDuration;
+
// Offset to the window of all layers in the token, for use by
// AppWindowToken animations.
int animLayerAdjustment;
@@ -287,6 +293,10 @@
pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
pw.print(" allDrawn="); pw.print(allDrawn);
pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
+ if (lastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("lastFreezeDuration=");
+ TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
+ }
if (animating || animation != null) {
pw.print(prefix); pw.print("animating="); pw.println(animating);
pw.print(prefix); pw.print("animation="); pw.println(animation);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 3964782..054a075 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -70,6 +70,7 @@
int mAboveUniverseLayer = 0;
int mBulkUpdateParams = 0;
+ Object mLastWindowFreezeSource;
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
new SparseArray<WindowAnimator.DisplayContentsAnimator>();
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index af603fd..1d1fda5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -43,6 +43,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import android.app.AppOpsManager;
+import android.util.TimeUtils;
import android.view.IWindowId;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -464,6 +465,9 @@
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
+ long mDisplayFreezeTime = 0;
+ int mLastDisplayFreezeDuration = 0;
+ Object mLastFinishedFreezeSource = null;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
boolean mClientFreezingScreen = false;
@@ -582,6 +586,7 @@
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
boolean mOrientationChangeComplete = true;
+ Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private boolean mObscured = false;
boolean mDimming = false;
@@ -3590,7 +3595,10 @@
synchronized(mWindowMap) {
mCurConfiguration = new Configuration(config);
- mWaitingForConfig = false;
+ if (mWaitingForConfig) {
+ mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
+ }
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -4209,6 +4217,7 @@
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
unfrozeWindows = true;
w.mDisplayContent.layoutNeeded = true;
}
@@ -4216,7 +4225,10 @@
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
wtoken.mAppAnimator.freezingScreen = false;
+ wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
mAppsFreezingScreen--;
+ mLastFinishedFreezeSource = wtoken;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
@@ -4242,6 +4254,7 @@
if (!wtoken.hiddenRequested) {
if (!wtoken.mAppAnimator.freezingScreen) {
wtoken.mAppAnimator.freezingScreen = true;
+ wtoken.mAppAnimator.lastFreezeDuration = 0;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
startFreezingDisplayLocked(false, 0, 0);
@@ -4750,6 +4763,7 @@
synchronized(mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client";
final long origId = Binder.clearCallingIdentity();
try {
stopFreezingDisplayLocked();
@@ -5742,6 +5756,7 @@
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
}
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
@@ -6240,6 +6255,7 @@
if (config == null && mWaitingForConfig) {
// Nothing changed but we are waiting for something... stop that!
mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
performLayoutAndPlaceSurfacesLocked();
}
return config;
@@ -7036,6 +7052,8 @@
WindowState w = windows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
Slog.w(TAG, "Force clearing orientation change: " + w);
}
}
@@ -7112,6 +7130,7 @@
synchronized (mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client-timeout";
stopFreezingDisplayLocked();
}
}
@@ -8029,6 +8048,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Changing surface while display frozen: " + w);
w.mOrientationChanging = true;
+ w.mLastFreezeDuration = 0;
mInnerFields.mOrientationChangeComplete = false;
if (!mWindowsFreezingScreen) {
mWindowsFreezingScreen = true;
@@ -8417,6 +8437,8 @@
"Orientation not waiting for draw in "
+ w + ", surface " + winAnimator.mSurfaceControl);
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
}
}
@@ -8930,6 +8952,8 @@
winAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
win.mOrientationChanging = false;
+ win.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
mResizingWindows.remove(i);
}
@@ -8940,6 +8964,7 @@
if (mInnerFields.mOrientationChangeComplete) {
if (mWindowsFreezingScreen) {
mWindowsFreezingScreen = false;
+ mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
}
stopFreezingDisplayLocked();
@@ -9226,6 +9251,7 @@
mInnerFields.mOrientationChangeComplete = false;
} else {
mInnerFields.mOrientationChangeComplete = true;
+ mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource;
if (mWindowsFreezingScreen) {
doRequest = true;
}
@@ -9498,6 +9524,8 @@
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
+ mDisplayFreezeTime = SystemClock.elapsedRealtime();
+ mLastFinishedFreezeSource = null;
mInputMonitor.freezeInputDispatchingLw();
@@ -9552,6 +9580,15 @@
}
mDisplayFrozen = false;
+ mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Screen frozen for ");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb);
+ if (mLastFinishedFreezeSource != null) {
+ sb.append(" due to ");
+ sb.append(mLastFinishedFreezeSource);
+ }
+ Slog.i(TAG, sb.toString());
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
@@ -10076,6 +10113,13 @@
}
pw.print(" mInTouchMode="); pw.print(mInTouchMode);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(" mLastDisplayFreezeDuration=");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
+ if ( mLastFinishedFreezeSource != null) {
+ pw.print(" due to ");
+ pw.print(mLastFinishedFreezeSource);
+ }
+ pw.println();
if (dumpAll) {
pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString());
pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 506fcec..ca060f4 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -26,6 +26,7 @@
import android.app.AppOpsManager;
import android.os.RemoteCallbackList;
+import android.util.TimeUtils;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
import com.android.server.input.InputWindowHandle;
@@ -266,6 +267,11 @@
*/
boolean mOrientationChanging;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int mLastFreezeDuration;
+
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -1387,6 +1393,10 @@
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
}
+ if (mLastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("mLastFreezeDuration=");
+ TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
+ }
if (mHScale != 1 || mVScale != 1) {
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 3a9f7cb..c07174b 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -505,20 +505,20 @@
public void setAlpha(float alpha) {
super.setAlpha(alpha);
if (alpha != mSurfaceTraceAlpha) {
+ mSurfaceTraceAlpha = alpha;
Slog.v(SURFACE_TAG, "setAlpha: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mSurfaceTraceAlpha = alpha;
}
@Override
public void setLayer(int zorder) {
super.setLayer(zorder);
if (zorder != mLayer) {
+ mLayer = zorder;
Slog.v(SURFACE_TAG, "setLayer: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mLayer = zorder;
sSurfaces.remove(this);
int i;
@@ -535,20 +535,20 @@
public void setPosition(float x, float y) {
super.setPosition(x, y);
if (x != mPosition.x || y != mPosition.y) {
+ mPosition.set(x, y);
Slog.v(SURFACE_TAG, "setPosition: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mPosition.set(x, y);
}
@Override
public void setSize(int w, int h) {
super.setSize(w, h);
if (w != mSize.x || h != mSize.y) {
+ mSize.set(w, h);
Slog.v(SURFACE_TAG, "setSize: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mSize.set(w, h);
}
@Override
@@ -556,10 +556,10 @@
super.setWindowCrop(crop);
if (crop != null) {
if (!crop.equals(mWindowCrop)) {
+ mWindowCrop.set(crop);
Slog.v(SURFACE_TAG, "setWindowCrop: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mWindowCrop.set(crop);
}
}
@@ -567,28 +567,28 @@
public void setLayerStack(int layerStack) {
super.setLayerStack(layerStack);
if (layerStack != mLayerStack) {
+ mLayerStack = layerStack;
Slog.v(SURFACE_TAG, "setLayerStack: " + this + ". Called by " + Debug.getCallers(3));
}
- mLayerStack = layerStack;
}
@Override
public void hide() {
super.hide();
if (mShown) {
+ mShown = false;
Slog.v(SURFACE_TAG, "hide: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mShown = false;
}
@Override
public void show() {
super.show();
if (!mShown) {
+ mShown = true;
Slog.v(SURFACE_TAG, "show: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mShown = true;
}
@Override
@@ -1307,6 +1307,7 @@
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ mAnimator.mLastWindowFreezeSource = w;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index d097a93..b313d48 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -32,6 +32,7 @@
libandroid_runtime \
libandroidfw \
libcutils \
+ liblog \
libhardware \
libhardware_legacy \
libnativehelper \
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 036fc43..98de12a 100644
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -43,6 +43,12 @@
static jmethodID method_requestRefLocation;
static jmethodID method_requestSetID;
static jmethodID method_requestUtcTime;
+static jmethodID method_reportGeofenceTransition;
+static jmethodID method_reportGeofenceStatus;
+static jmethodID method_reportGeofenceAddStatus;
+static jmethodID method_reportGeofenceRemoveStatus;
+static jmethodID method_reportGeofencePauseStatus;
+static jmethodID method_reportGeofenceResumeStatus;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -50,6 +56,7 @@
static const GpsNiInterface* sGpsNiInterface = NULL;
static const GpsDebugInterface* sGpsDebugInterface = NULL;
static const AGpsRilInterface* sAGpsRilInterface = NULL;
+static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL;
// temporary storage for GPS callbacks
static GpsSvStatus sGpsSvStatus;
@@ -107,7 +114,7 @@
static void set_capabilities_callback(uint32_t capabilities)
{
- ALOGD("set_capabilities_callback: %ld\n", capabilities);
+ ALOGD("set_capabilities_callback: %du\n", capabilities);
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -233,6 +240,97 @@
create_thread_callback,
};
+static void gps_geofence_transition_callback(int32_t geofence_id, GpsLocation* location,
+ int32_t transition, GpsUtcTime timestamp)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, geofence_id,
+ location->flags, (jdouble)location->latitude, (jdouble)location->longitude,
+ (jdouble)location->altitude,
+ (jfloat)location->speed, (jfloat)location->bearing,
+ (jfloat)location->accuracy, (jlong)location->timestamp,
+ transition, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_status_callback(int32_t status, GpsLocation* location)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint flags = 0;
+ jdouble latitude = 0;
+ jdouble longitude = 0;
+ jdouble altitude = 0;
+ jfloat speed = 0;
+ jfloat bearing = 0;
+ jfloat accuracy = 0;
+ jlong timestamp = 0;
+ if (location != NULL) {
+ flags = location->flags;
+ latitude = location->latitude;
+ longitude = location->longitude;
+ altitude = location->altitude;
+ speed = location->speed;
+ bearing = location->bearing;
+ accuracy = location->accuracy;
+ timestamp = location->timestamp;
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
+ flags, latitude, longitude, altitude, speed, bearing, accuracy, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_add_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_add_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceAddStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_remove_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_remove_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceRemoveStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_resume_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_resume_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceResumeStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_pause_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_pause_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofencePauseStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+GpsGeofenceCallbacks sGpsGeofenceCallbacks = {
+ gps_geofence_transition_callback,
+ gps_geofence_status_callback,
+ gps_geofence_add_callback,
+ gps_geofence_remove_callback,
+ gps_geofence_pause_callback,
+ gps_geofence_resume_callback,
+ create_thread_callback,
+};
+
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
@@ -249,6 +347,18 @@
method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
+ method_reportGeofenceTransition = env->GetMethodID(clazz,"reportGeofenceTransition",
+ "(IIDDDFFFJIJ)V");
+ method_reportGeofenceStatus = env->GetMethodID(clazz,"reportGeofenceStatus",
+ "(IIDDDFFFJ)V");
+ method_reportGeofenceAddStatus = env->GetMethodID(clazz,"reportGeofenceAddStatus",
+ "(II)V");
+ method_reportGeofenceRemoveStatus = env->GetMethodID(clazz,"reportGeofenceRemoveStatus",
+ "(II)V");
+ method_reportGeofenceResumeStatus = env->GetMethodID(clazz,"reportGeofenceResumeStatus",
+ "(II)V");
+ method_reportGeofencePauseStatus = env->GetMethodID(clazz,"reportGeofencePauseStatus",
+ "(II)V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
@@ -270,6 +380,8 @@
(const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
sAGpsRilInterface =
(const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
+ sGpsGeofencingInterface =
+ (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE);
}
}
@@ -287,7 +399,7 @@
if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
return false;
- // if XTRA initialization fails we will disable it by sGpsXtraInterface to null,
+ // if XTRA initialization fails we will disable it by sGpsXtraInterface to NULL,
// but continue to allow the rest of the GPS interface to work.
if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
sGpsXtraInterface = NULL;
@@ -297,6 +409,8 @@
sGpsNiInterface->init(&sGpsNiCallbacks);
if (sAGpsRilInterface)
sAGpsRilInterface->init(&sAGpsRilCallbacks);
+ if (sGpsGeofencingInterface)
+ sGpsGeofencingInterface->init(&sGpsGeofenceCallbacks);
return true;
}
@@ -565,6 +679,62 @@
}
}
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* env,
+ jobject obj) {
+ if (sGpsGeofencingInterface != NULL) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jdouble latitude, jdouble longitude, jdouble radius,
+ jint last_transition, jint monitor_transition, jint notification_responsiveness,
+ jint unknown_timer) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->add_geofence_area(geofence_id, latitude, longitude,
+ radius, last_transition, monitor_transition, notification_responsiveness,
+ unknown_timer);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_remove_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->remove_geofence_area(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_pause_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->pause_geofence(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jint monitor_transition) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->resume_geofence(geofence_id, monitor_transition);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -591,6 +761,11 @@
{"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message},
{"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
{"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state },
+ {"native_is_geofence_supported", "()Z", (void*) android_location_GpsLocationProvider_is_geofence_supported},
+ {"native_add_geofence", "(IDDDIIII)Z", (void *)android_location_GpsLocationProvider_add_geofence},
+ {"native_remove_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_remove_geofence},
+ {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence},
+ {"native_resume_geofence", "(II)Z", (void *)android_location_GpsLocationProvider_resume_geofence}
};
int register_android_server_location_GpsLocationProvider(JNIEnv* env)
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 6e2a70d..31e01c0 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -162,13 +162,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CdmaCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyCdma:{");
sb.append(" mNetworkId="); sb.append(mNetworkId);
sb.append(" mSystemId="); sb.append(mSystemId);
sb.append(" mBasestationId="); sb.append(mBasestationId);
sb.append(" mLongitude="); sb.append(mLongitude);
sb.append(" mLatitude="); sb.append(mLatitude);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index bda96be..98113e7 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -147,13 +147,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("GsmCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyGsm:{");
sb.append(" mMcc=").append(mMcc);
- sb.append(" mMnc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
sb.append(" mPsc=").append(mPsc);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index f72d583..86924bd 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -142,13 +142,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("LteCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyLte:{");
sb.append(" mMcc="); sb.append(mMcc);
sb.append(" mMnc="); sb.append(mMnc);
sb.append(" mCi="); sb.append(mCi);
sb.append(" mPci="); sb.append(mPci);
sb.append(" mTac="); sb.append(mTac);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index f367f99..fe3c68b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -149,7 +149,7 @@
StringBuffer sb = new StringBuffer();
String timeStampType;
- sb.append(" mRegistered=").append(mRegistered ? "YES" : "NO");
+ sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
timeStampType = timeStampTypeToString(mTimeStampType);
sb.append(" mTimeStampType=").append(timeStampType);
sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index a5d6e9c..6f2f1f6 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -87,10 +87,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoCdma:");
+ sb.append("CellInfoCdma:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityCdma);
- sb.append(", ").append(mCellSignalStrengthCdma);
+ sb.append(" ").append(mCellIdentityCdma);
+ sb.append(" ").append(mCellSignalStrengthCdma);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index bf0eca8..1bedddb 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -87,10 +87,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoGsm:");
+ sb.append("CellInfoGsm:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityGsm);
- sb.append(", ").append(mCellSignalStrengthGsm);
+ sb.append(" ").append(mCellIdentityGsm);
+ sb.append(" ").append(mCellSignalStrengthGsm);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index d7a58b6..287c9f0 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -91,10 +91,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoLte:");
+ sb.append("CellInfoLte:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityLte);
- sb.append(", ").append(mCellSignalStrengthLte);
+ sb.append(" ").append(mCellIdentityLte);
+ sb.append(" ").append(mCellSignalStrengthLte);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4aee902..6400e68 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1341,7 +1341,8 @@
}
/**
- * Returns all observed cell information of the device.
+ * Returns all observed cell information of the device. This does
+ * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
*
* @return List of CellInfo or null if info unavailable.
*
@@ -1357,4 +1358,24 @@
return null;
}
}
+
+ /**
+ * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
+ * PhoneStateListener.onCellInfoChanged} will be invoked.
+ *
+ * The default, 0, means invoke onCellInfoChanged when any of the reported
+ * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+ * A onCellInfoChanged.
+ *
+ * @param rateInMillis the rate
+ *
+ * @hide
+ */
+ public void setCellInfoListRate(int rateInMillis) {
+ try {
+ getITelephony().setCellInfoListRate(rateInMillis);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1449ab1..b78f589 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -294,5 +294,10 @@
* Returns the all observed cell information of the device.
*/
List<CellInfo> getAllCellInfo();
+
+ /**
+ * Sets minimum time in milli-seconds between onCellInfoChanged
+ */
+ void setCellInfoListRate(int rateInMillis);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 077ad68..9650b99 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -260,6 +260,8 @@
int RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU = 106;
int RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS = 107;
int RIL_REQUEST_VOICE_RADIO_TECH = 108;
+ int RIL_REQUEST_GET_CELL_INFO_LIST = 109;
+ int RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE = 110;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
@@ -297,4 +299,5 @@
int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
int RIL_UNSOL_RIL_CONNECTED = 1034;
int RIL_UNSOL_VOICE_RADIO_TECH_CHANGED = 1035;
+ int RIL_UNSOL_CELL_INFO_LIST = 1036;
}
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index 4d60c83..7ae0fb8 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -127,6 +127,10 @@
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent = mPackageManager.getLaunchIntentForPackage(packageName);
+ // Skip if the apk does not have a launch intent.
+ if (intent == null) {
+ return null;
+ }
// We check for any Crash or ANR dialogs that are already up, and we ignore them. This is
// so that we don't report crashes that were caused by prior apps (which those particular
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 5b88669..9b1658a 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -39,14 +39,14 @@
LOCAL_C_INCLUDES += external/zlib
LOCAL_C_INCLUDES += build/libs/host/include
-#LOCAL_WHOLE_STATIC_LIBRARIES :=
LOCAL_STATIC_LIBRARIES := \
libhost \
libandroidfw \
libutils \
libcutils \
libexpat \
- libpng
+ libpng \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -ldl -lpthread
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index dd57ae6..ad8de69 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -20,7 +20,8 @@
LOCAL_STATIC_LIBRARIES := \
libutils \
libandroidfw \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
index fce2e93..90fbc08 100644
--- a/tools/validatekeymaps/Android.mk
+++ b/tools/validatekeymaps/Android.mk
@@ -20,7 +20,8 @@
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
libutils \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 47f1fbf..7b1a71f 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1196,7 +1196,7 @@
WifiConfiguration newConfig) {
boolean ipChanged = false;
boolean proxyChanged = false;
- LinkProperties linkProperties = new LinkProperties();
+ LinkProperties linkProperties = null;
switch (newConfig.ipAssignment) {
case STATIC:
@@ -1262,10 +1262,10 @@
}
if (!ipChanged) {
- addIpSettingsFromConfig(linkProperties, currentConfig);
+ linkProperties = copyIpSettingsFromConfig(currentConfig);
} else {
currentConfig.ipAssignment = newConfig.ipAssignment;
- addIpSettingsFromConfig(linkProperties, newConfig);
+ linkProperties = copyIpSettingsFromConfig(newConfig);
log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
linkProperties.toString());
}
@@ -1291,8 +1291,9 @@
return new NetworkUpdateResult(ipChanged, proxyChanged);
}
- private void addIpSettingsFromConfig(LinkProperties linkProperties,
- WifiConfiguration config) {
+ private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(config.linkProperties.getInterfaceName());
for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
linkProperties.addLinkAddress(linkAddr);
}
@@ -1302,6 +1303,7 @@
for (InetAddress dns : config.linkProperties.getDnses()) {
linkProperties.addDns(dns);
}
+ return linkProperties;
}
/**